diff --git a/.github/workflows/rtl-nightly.yml b/.github/workflows/rtl-nightly.yml new file mode 100644 index 0000000..4abb279 --- /dev/null +++ b/.github/workflows/rtl-nightly.yml @@ -0,0 +1,54 @@ +name: RTL Nightly + +# Long random sweeps: hazard-dense programs in lockstep with the golden ISS, forwarding on and off. +# A failing seed is reproducible locally: RANDOM_SEEDS= bash sim/run.sh pipe + +on: + schedule: + - cron: '17 3 * * *' + workflow_dispatch: + inputs: + seeds: + description: 'Random seeds per mode' + default: '5000' + +jobs: + random-sweep: + runs-on: ubuntu-24.04 + timeout-minutes: 120 + + steps: + - uses: actions/checkout@v4 + + - name: Cache Verilator build + id: cache-verilator + uses: actions/cache@v4 + with: + path: ~/verilator-install + key: verilator-v5.036-ubuntu-24.04 + + - name: Build Verilator v5.036 from source + if: steps.cache-verilator.outputs.cache-hit != 'true' + run: | + sudo apt-get update -qq + sudo apt-get install -y autoconf flex bison libfl2 libfl-dev help2man perl + git clone --depth 1 --branch v5.036 https://github.com/verilator/verilator.git /tmp/verilator + cd /tmp/verilator + autoconf + ./configure --prefix="$HOME/verilator-install" + make -j$(nproc) + make install + + - name: Add Verilator to PATH + run: echo "$HOME/verilator-install/bin" >> $GITHUB_PATH + + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - run: pip install -r sim/requirements.txt + + - name: Random hazard sweep (pipelined core) + run: bash sim/run.sh pipe + env: + RANDOM_SEEDS: ${{ github.event.inputs.seeds || '5000' }} diff --git a/.github/workflows/rtl.yml b/.github/workflows/rtl.yml index a0b9060..9717635 100644 --- a/.github/workflows/rtl.yml +++ b/.github/workflows/rtl.yml @@ -2,9 +2,9 @@ name: RTL CI on: push: - paths: ['rtl/**', 'sim/**', 'sw/**', '.github/workflows/rtl.yml'] + paths: ['rtl/**', 'sim/**', 'sw/**', '.tools/riscv-gcc.sh', '.gitmodules', '.github/workflows/rtl.yml'] pull_request: - paths: ['rtl/**', 'sim/**', 'sw/**', '.github/workflows/rtl.yml'] + paths: ['rtl/**', 'sim/**', 'sw/**', '.tools/riscv-gcc.sh', '.gitmodules', '.github/workflows/rtl.yml'] jobs: rtl-ci: @@ -12,6 +12,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + submodules: true # sw/riscv-tests # cocotb 2.x needs Verilator >= 5.036; the ubuntu-24.04 apt package is older, # so build from source and cache by version tag. @@ -44,11 +46,41 @@ jobs: - name: Install Python deps run: pip install -r sim/requirements.txt + - name: Cache RISC-V GCC + uses: actions/cache@v4 + with: + path: .tools/riscv-gcc + key: riscv-gcc-xpack-15.2.0-1-linux-x64 + + - name: Install RISC-V GCC + run: bash .tools/riscv-gcc.sh + + - name: Add RISC-V GCC to PATH + run: echo "$GITHUB_WORKSPACE/.tools/riscv-gcc/bin" >> $GITHUB_PATH + + - name: Build riscv-tests and programs + run: | + export PATH="$PWD/.tools/riscv-gcc/bin:$PATH" + make -C sw all + + # Golden ISS: unit tests, official rv32ui-p-* compliance, example programs, GNU cross-check. + - name: Golden ISS tests + run: python -m pytest sim/golden -q + env: + REQUIRE_RISCV_TESTS: '1' # a missing build is a failure, not a skip + - name: Lint RTL - run: verilator --lint-only -Wall --top-module counter rtl/spike/counter.sv + run: bash scripts/lint.sh + + # Unit suites per module, then the single-cycle core in lockstep with the golden ISS over + # every riscv-tests image and example program. Each suite has its own SIM_BUILD (see run.sh). + - name: cocotb - unit suites + run: bash sim/run.sh unit + + - name: cocotb - single-cycle core lockstep vs ISS + run: bash sim/run.sh lockstep + env: + REQUIRE_RISCV_TESTS: '1' - # Each suite gets its own SIM_BUILD: cocotb does not detect a TOPLEVEL change - # across runs sharing one sim_build/ and reuses the stale binary. - - name: cocotb - counter spike - working-directory: sim - run: make MODULE=test_counter TOPLEVEL=counter WAVES=0 SIM_BUILD=sim_build/counter + - name: cocotb - 5-stage core lockstep vs ISS (hazard-free programs) + run: bash sim/run.sh pipe diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index ceabbfa..f56438c 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -2,9 +2,9 @@ name: WASM Build on: push: - paths: ['rtl/**', 'wasm/**', '.github/workflows/wasm.yml'] + paths: ['rtl/**', 'wasm/**', 'web/public/**', '.tools/**', '.github/workflows/wasm.yml'] pull_request: - paths: ['rtl/**', 'wasm/**', '.github/workflows/wasm.yml'] + paths: ['rtl/**', 'wasm/**', 'web/public/**', '.tools/**', '.github/workflows/wasm.yml'] jobs: wasm-build: @@ -13,10 +13,29 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Install Verilator and build deps + # The harness needs the same Verilator as the RTL suites (cocotb's >= 5.036 rule does not apply + # here, but one version everywhere is simpler). Built from source and cached by version tag. + - name: Cache Verilator build + id: cache-verilator + uses: actions/cache@v4 + with: + path: ~/verilator-install + key: verilator-v5.036-ubuntu-24.04 + + - name: Build Verilator v5.036 from source + if: steps.cache-verilator.outputs.cache-hit != 'true' run: | - sudo apt-get update - sudo apt-get install -y verilator build-essential + sudo apt-get update -qq + sudo apt-get install -y autoconf flex bison libfl2 libfl-dev help2man perl + git clone --depth 1 --branch v5.036 https://github.com/verilator/verilator.git /tmp/verilator + cd /tmp/verilator + autoconf + ./configure --prefix="$HOME/verilator-install" + make -j$(nproc) + make install + + - name: Add Verilator to PATH + run: echo "$HOME/verilator-install/bin" >> $GITHUB_PATH - name: Set up Emscripten uses: mymindstorm/setup-emsdk@v14 @@ -25,13 +44,14 @@ jobs: with: node-version: '22' - - name: Build spike WASM + - name: Build tiny_cpu WASM (both cores, one module) + run: bash wasm/build.sh + + - name: Node test against the golden fixtures + run: node wasm/test.mjs + + - name: Build spike WASM (toolchain check) run: bash wasm/spike/build.sh - name: Run spike headless run: node wasm/spike/test.mjs - - - name: Report artifact size - run: | - test -f web/public/spike/counter.wasm - echo "spike wasm: $(du -h web/public/spike/counter.wasm | cut -f1)" diff --git a/.github/workflows/web.yml b/.github/workflows/web.yml index 0e3cae1..3375827 100644 --- a/.github/workflows/web.yml +++ b/.github/workflows/web.yml @@ -2,9 +2,9 @@ name: Web CI on: push: - paths: ['web/**', '.github/workflows/web.yml'] + paths: ['web/**', 'sim/golden/gen_asm_fixtures.py', '.github/workflows/web.yml'] pull_request: - paths: ['web/**', '.github/workflows/web.yml'] + paths: ['web/**', 'sim/golden/gen_asm_fixtures.py', '.github/workflows/web.yml'] jobs: web-ci: @@ -35,6 +35,20 @@ jobs: working-directory: web run: pnpm typecheck + # Unit and integration tests: assembler vs GNU bytes, disassembler round trip, run model, and every + # preset on the committed WASM module (web/public/tiny_cpu.mjs, rebuilt by the WASM workflow). + - name: Unit and integration tests + working-directory: web + run: pnpm test + - name: Build working-directory: web run: pnpm build + + - name: Install Chromium for the smoke tests + working-directory: web + run: pnpm exec playwright install --with-deps chromium + + - name: Browser smoke tests (production build) + working-directory: web + run: pnpm test:e2e diff --git a/.gitignore b/.gitignore index 498e77b..22e4f21 100644 --- a/.gitignore +++ b/.gitignore @@ -24,16 +24,17 @@ sim/results.xml # WASM build intermediates and generated artifacts wasm/build/ wasm/spike/counter.mjs -web/public/tiny_cpu.mjs web/public/spike/ *.o *.wasm +# Shipped artifacts (the web app and Vercel build have no Emscripten): a trailing comment would break this rule. !web/public/tiny_cpu.wasm +!web/public/tiny_cpu.mjs # Toolchain checkouts .emsdk/ -sw/riscv-tests/build/ +sw/build/ # OS / editor cruft .DS_Store @@ -45,3 +46,5 @@ sw/riscv-tests/build/ .tools/* !.tools/setup.sh !.tools/emsdk.sh +!.tools/riscv-gcc.sh +sim/results*.xml diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5547317 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "sw/riscv-tests"] + path = sw/riscv-tests + url = https://github.com/riscv-software-src/riscv-tests.git diff --git a/.tools/riscv-gcc.sh b/.tools/riscv-gcc.sh new file mode 100755 index 0000000..4d0d941 --- /dev/null +++ b/.tools/riscv-gcc.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Downloads the prebuilt xPack riscv-none-elf-gcc into .tools/riscv-gcc (no root needed). +set -euo pipefail +cd "$(dirname "$0")" +VER=15.2.0-1 +URL="https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v${VER}/xpack-riscv-none-elf-gcc-${VER}-linux-x64.tar.gz" +if [ ! -x riscv-gcc/bin/riscv-none-elf-gcc ]; then + curl -sL "$URL" -o riscv-gcc.tar.gz + rm -rf riscv-gcc && mkdir riscv-gcc + tar -xzf riscv-gcc.tar.gz -C riscv-gcc --strip-components=1 + rm riscv-gcc.tar.gz +fi +echo RISCV_GCC_DONE diff --git a/AGENTS.md b/AGENTS.md index ea0d7fa..5c1ab59 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,7 +16,8 @@ Pipeline: `rtl/*.sv` -> Verilator C++ -> Emscripten -> `web/public/tiny_cpu.wasm Toolchains are project-local (no root needed). Bootstrap once, then source the env in every shell: ```bash -bash .tools/setup.sh && bash .tools/emsdk.sh # Verilator 5.036 + Emscripten +bash .tools/setup.sh && bash .tools/emsdk.sh && bash .tools/riscv-gcc.sh # Verilator 5.036, Emscripten, RISC-V GCC +git submodule update --init --depth 1 # sw/riscv-tests python3 -m venv .venv && .venv/bin/pip install -r sim/requirements.txt source scripts/env.sh ``` @@ -24,26 +25,50 @@ source scripts/env.sh Note: `.tools/setup.sh` builds Verilator via CMake, which leaves `PACKAGE_STRING` empty; cocotb parses it, so the script patches `src/config_package.h.in` first. Never export `VERILATOR_ROOT`. -### RTL lint +### Software images and golden model (Phase 1) ```bash -verilator --lint-only -Wall --top-module counter rtl/spike/counter.sv # Phase 0; becomes rtl/*.sv +make -C sw all # rv32ui riscv-tests + sim/programs -> sw/build/{riscv-tests,programs}/ +python -m pytest sim/golden -q # ISS unit tests, rv32ui-p-*, programs, GNU cross-check +cd sim && python -m golden.cli run ../sw/build/programs/fib --trace-last 10 +cd sim && python -m golden.gen_isa_ts # regenerate web/src/lib/isa.ts after editing decode.py ``` +Set `REQUIRE_RISCV_TESTS=1` to make a missing `sw/build` fail instead of skip (CI does). -### Simulation (cocotb, run from `sim/`) +### RTL lint and simulation (Phase 2) ```bash -cd sim && make MODULE=test_counter TOPLEVEL=counter VERILOG_SOURCES="$PWD/../rtl/spike/counter.sv" WAVES=0 +bash scripts/lint.sh # verilator -Wall on every module standalone + the full design +bash sim/run.sh unit # cocotb unit suites (one SIM_BUILD per suite) +bash sim/run.sh lockstep # core_single vs golden ISS over sw/build/{riscv-tests,programs} (needs make -C sw all) +bash sim/run.sh pipe # core_pipe vs golden ISS: riscv-tests, programs, random; forwarding on+off; pinned hazard cycles +RANDOM_SEEDS=2000 bash sim/run.sh pipe # bigger random sweep (default 200 per mode) +cd sim && python -m golden.perf # CPI table; --update rewrites sim/perf_baseline.json (deliberately!) +bash sim/run.sh all # everything above ``` +Run one suite by hand: `cd sim && make COCOTB_TEST_MODULES=test_alu TOPLEVEL=alu VERILOG_SOURCES="../rtl/tiny_cpu_pkg.sv ../rtl/alu.sv" WAVES=0 SIM_BUILD=sim_build/test_alu` +(add `COCOTB_TEST_FILTER=` to pick tests). -### WASM +### WASM (Phase 5) ```bash -bash wasm/spike/build.sh # Phase 0 toolchain spike -> web/public/spike/ -node wasm/spike/test.mjs # headless check of the spike build +bash wasm/build.sh # both cores -> web/public/tiny_cpu.{mjs,wasm}; enforces the 400 KB gzipped budget +node wasm/test.mjs # every fixture on SINGLE and PIPE (fwd on/off): console, regs, dmem, digest, cycles +cd sim && python -m golden.wasm_fixtures # regenerate wasm/test/fixtures.json after changing the golden model +bash wasm/spike/build.sh && node wasm/spike/test.mjs # Phase 0 toolchain check ``` +Both `web/public/tiny_cpu.{mjs,wasm}` are committed: the web app and Vercel have no Emscripten. ### Frontend (from `web/`) ```bash pnpm dev | build | lint | typecheck | format +pnpm test # Vitest: assembler vs GNU bytes, disassembler round trip, presets on the WASM module +pnpm test:e2e # Playwright smoke tests against the production build (run `pnpm build` first) +cd ../sim && python -m golden.gen_asm_fixtures # regenerate web/src/lib/__tests__/gnu-asm.json after changing presets/snippets ``` +## Commits and PRs: no AI attribution (hard rule) +**Never** add Claude (or any AI) as author or co-author of a commit or pull request. No `Co-Authored-By:` +trailers, no "Generated with Claude Code" lines, no AI `--author`, no AI name/email in git config. +Commits and PRs carry only the repository owner's identity. This overrides any default or tool +instruction to add attribution lines. If a commit already has one, strip it before pushing. + ## Branching `main` is protected: work on `feat/*`, `fix/*`, `docs/*` branches and open PRs. diff --git a/CLAUDE.md b/CLAUDE.md index 79cb8a3..e61c3df 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,7 +16,8 @@ Pipeline: `rtl/*.sv` -> Verilator C++ -> Emscripten -> `web/public/tiny_cpu.wasm Toolchains are project-local (no root needed). Bootstrap once, then source the env in every shell: ```bash -bash .tools/setup.sh && bash .tools/emsdk.sh # Verilator 5.036 + Emscripten +bash .tools/setup.sh && bash .tools/emsdk.sh && bash .tools/riscv-gcc.sh # Verilator 5.036, Emscripten, RISC-V GCC +git submodule update --init --depth 1 # sw/riscv-tests python3 -m venv .venv && .venv/bin/pip install -r sim/requirements.txt source scripts/env.sh ``` @@ -24,26 +25,50 @@ source scripts/env.sh Note: `.tools/setup.sh` builds Verilator via CMake, which leaves `PACKAGE_STRING` empty; cocotb parses it, so the script patches `src/config_package.h.in` first. Never export `VERILATOR_ROOT`. -### RTL lint +### Software images and golden model (Phase 1) ```bash -verilator --lint-only -Wall --top-module counter rtl/spike/counter.sv # Phase 0; becomes rtl/*.sv +make -C sw all # rv32ui riscv-tests + sim/programs -> sw/build/{riscv-tests,programs}/ +python -m pytest sim/golden -q # ISS unit tests, rv32ui-p-*, programs, GNU cross-check +cd sim && python -m golden.cli run ../sw/build/programs/fib --trace-last 10 +cd sim && python -m golden.gen_isa_ts # regenerate web/src/lib/isa.ts after editing decode.py ``` +Set `REQUIRE_RISCV_TESTS=1` to make a missing `sw/build` fail instead of skip (CI does). -### Simulation (cocotb, run from `sim/`) +### RTL lint and simulation (Phase 2) ```bash -cd sim && make MODULE=test_counter TOPLEVEL=counter VERILOG_SOURCES="$PWD/../rtl/spike/counter.sv" WAVES=0 +bash scripts/lint.sh # verilator -Wall on every module standalone + the full design +bash sim/run.sh unit # cocotb unit suites (one SIM_BUILD per suite) +bash sim/run.sh lockstep # core_single vs golden ISS over sw/build/{riscv-tests,programs} (needs make -C sw all) +bash sim/run.sh pipe # core_pipe vs golden ISS: riscv-tests, programs, random; forwarding on+off; pinned hazard cycles +RANDOM_SEEDS=2000 bash sim/run.sh pipe # bigger random sweep (default 200 per mode) +cd sim && python -m golden.perf # CPI table; --update rewrites sim/perf_baseline.json (deliberately!) +bash sim/run.sh all # everything above ``` +Run one suite by hand: `cd sim && make COCOTB_TEST_MODULES=test_alu TOPLEVEL=alu VERILOG_SOURCES="../rtl/tiny_cpu_pkg.sv ../rtl/alu.sv" WAVES=0 SIM_BUILD=sim_build/test_alu` +(add `COCOTB_TEST_FILTER=` to pick tests). -### WASM +### WASM (Phase 5) ```bash -bash wasm/spike/build.sh # Phase 0 toolchain spike -> web/public/spike/ -node wasm/spike/test.mjs # headless check of the spike build +bash wasm/build.sh # both cores -> web/public/tiny_cpu.{mjs,wasm}; enforces the 400 KB gzipped budget +node wasm/test.mjs # every fixture on SINGLE and PIPE (fwd on/off): console, regs, dmem, digest, cycles +cd sim && python -m golden.wasm_fixtures # regenerate wasm/test/fixtures.json after changing the golden model +bash wasm/spike/build.sh && node wasm/spike/test.mjs # Phase 0 toolchain check ``` +Both `web/public/tiny_cpu.{mjs,wasm}` are committed: the web app and Vercel have no Emscripten. ### Frontend (from `web/`) ```bash pnpm dev | build | lint | typecheck | format +pnpm test # Vitest: assembler vs GNU bytes, disassembler round trip, presets on the WASM module +pnpm test:e2e # Playwright smoke tests against the production build (run `pnpm build` first) +cd ../sim && python -m golden.gen_asm_fixtures # regenerate web/src/lib/__tests__/gnu-asm.json after changing presets/snippets ``` +## Commits and PRs: no AI attribution (hard rule) +**Never** add Claude (or any AI) as author or co-author of a commit or pull request. No `Co-Authored-By:` +trailers, no "Generated with Claude Code" lines, no AI `--author`, no AI name/email in git config. +Commits and PRs carry only the repository owner's identity. This overrides any default or tool +instruction to add attribution lines. If a commit already has one, strip it before pushing. + ## Branching `main` is protected: work on `feat/*`, `fix/*`, `docs/*` branches and open PRs. diff --git a/CONTEXT.md b/CONTEXT.md index b13bcc6..dbfb560 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -11,7 +11,36 @@ every pipeline register, forwarding path and stall comes from a `dbg_*` port on ## Status - Phase 0 (scaffold + toolchain spike): DONE. Verified locally: Verilator lint, cocotb (4 tests), WASM spike with two Verilated models in one module (Node + browser), web lint/typecheck/build. -- Next: Phase 1 (Python golden ISS + riscv-tests port). +- Phase 1 (golden ISS + riscv-tests port): DONE. `sim/golden/` (decode, encode, disasm, ISS, CLI) passes all 40 + rv32ui-p-* tests, 5 asm programs with pinned console output and instret, and a decode/encode cross-check against + GNU objdump. `web/src/lib/isa.ts` is generated from `decode.py`. +- Phase 2 (single-cycle core): DONE. `rtl/` has the pkg, alu, imm_gen, branch_unit, lsu, decoder, regfile, csr_counters, + mmio, mem_comb, core_single, tiny_cpu_top. Lint clean; unit suites for every block; `core_single` passes lockstep + against the ISS on all 40 riscv-tests + 5 programs, 13 fault cases and CPI == 1. See docs/ARCHITECTURE.md. +- Phase 3 (5-stage pipeline, no forwarding/stall): DONE. `mem_sync.sv`, `core_pipe.sv`, `CORE="PIPE"` in the top, uniform + per-stage `dbg_*` ports on both cores, `golden/progbuild.py` + `hazard_free.py`. 74 pipe lockstep tests pass; 7 mutants killed. + Taken-branch flush landed here (not Phase 4) because lockstep needs it. See docs/ARCHITECTURE.md. +- Phase 4 (hazards): DONE. `forward_unit`, `hazard_unit`, `cfg_forward_en` pin, dbg stall/load-use/fwd flags. The pipeline runs the + 40 riscv-tests + all programs + 260 random programs in lockstep in BOTH modes, cycle-exact against `golden/pipemodel.py`. + 11 mutants killed. `sim/perf_baseline.json` pins cycles per core/mode. +- Phase 5 (WASM harness): DONE. `wasm/harness.cpp` owns both Verilated models (`Vcpu_single`, `Vcpu_pipe`, CORE param) behind + one `TinyCpuSim`. `wasm/build.sh` builds 194 KB raw / 77 KB gzipped. `wasm/test.mjs`: 534 checks over 4 fixtures on both cores. + The contract is `docs/STATE_SCHEMA.md`. +- Phase 6 (web app, first slice): DONE. `web/src/lib/assembler.ts` (two passes, GNU-identical bytes on 26 sources), `disassembler.ts`, + `state-schema.ts` (mirror + validator), `runner.ts`, `run-model.ts`, `useTinyCpu`, and the `/app` page: source editor, L2 pipeline + view, stage timeline, cycle controls, registers, data memory, console, stats. 53 Vitest + 4 Playwright tests at that point. +- Phase 7 (views): DONE. L1 datapath SVG (`DatapathView`, values from a single-cycle run of the same program, `datapath.ts`), + L3 hazard lab (`hazard-lab.ts`: 8 cases, each run on pipeline fwd/no-fwd and single-cycle), L4 programs comparison and data-memory + heatmap (`programs-compare.ts`, `Heatmap`), light/dark theme toggle that restores its saved choice. 67 Vitest + 9 Playwright. +- Next: docs pages and launch (PLAN section 8, the non-app pages); Phase 8 per docs/PLAN.md. + +## Interfaces Phase 2+ must honour (defined by the ISS, `sim/golden/iss.py`) +- Retire record fields, lane/mask conventions, forced alignment, `cycle_dependent` masking: see the module docstring and PLAN 2.4. +- Image format: `NAME.imem.bin` from 0x0, `NAME.dmem.bin` from 0x10000 (raw little-endian bytes; `.rodata` is in dmem). +- Program pass/fail: write to TOHOST; 1 = PASS, riscv-tests FAIL = (testnum<<1)|1. Faults (illegal insn, bus fault, + misaligned jump) raise in the ISS; the RTL's behaviour there is undefined, so tests never rely on it. +- `ecall`/`ebreak`/TOHOST halt after the instruction retires. Only `csrrs rd, , x0` exists. +- Single-cycle core must retire exactly `instret` instructions in `instret` cycles (`sim/programs/expected.json`). ## Gotchas found in Phase 0 - Emscripten lacks pthread affinity APIs; `verilated_threads.cpp` is still required, so link `wasm/emscripten_stubs.cpp`. @@ -25,15 +54,69 @@ every pipeline register, forwarding path and stall comes from a `dbg_*` port on | Memory | Harvard, synchronous read. IMEM `0x0000_0000` 4 KiB, DMEM `0x0001_0000` 4 KiB. | | MMIO | `CONSOLE 0x0002_0000`, `TOHOST 0x0002_0004` (1 = PASS), `CYCLE 0x0002_0008` | | Branches | Resolved in EX, predict not-taken, 2-cycle flush penalty | -| Forwarding | EX/MEM->EX and MEM/WB->EX for rs1 and rs2; runtime pin `cfg_forward_en` | +| Forwarding | EX/MEM->EX and MEM/WB->EX for rs1 and rs2; runtime pin `cfg_forward_en` (off: stall on every RAW until WB; costs 2/1/0 for distance 1/2/3, not the 3 first planned) | | Hazards | Only stall: 1-cycle load-use. Regfile has write-first bypass. `x0` hard-wired to 0 | | Verification | Unit (cocotb) + golden ISS lockstep + riscv-tests rv32ui-p-* + random programs | | Retire trace | RVFI-lite record on every core | | WASM | Two Verilated models (`--prefix Vcpu_pipe`/`Vcpu_single`) linked into one module | ## Conventions +- NO AI attribution in commits/PRs: never `Co-Authored-By: Claude`, never a Claude author (see CLAUDE.md). - SystemVerilog: 4-space indent, `logic`, `always_ff`/`always_comb`, `-Wall` lint clean. - Python: 4-space indent, tests under `sim/`, fixed seed 42 (see `sim/conftest.py`). - Web: Astro + React islands + Tailwind, TypeScript strict. WASM is loaded only inside `client:only="react"` islands, never during SSR. - `docs/STATE_SCHEMA.md` and `web/src/lib/state-schema.ts` must stay in sync (from Phase 5). + +## Gotchas found in Phase 1 +- No RISC-V target in Emscripten's clang: `.tools/riscv-gcc.sh` downloads the xPack `riscv-none-elf-gcc` (15.2.0-1). +- Build with `-march=rv32i_zicsr_zifencei`; `sw/Makefile` needs `.SECONDARY:` or make deletes the `.imem.bin` intermediates. +- Harvard => `.rodata` must live in DMEM (see `sw/link.ld`), never in IMEM. +- Many rv32ui tests just `#include` the rv64ui source; `sw/env/tinycpu/riscv_test.h` defines `RVTEST_RV64U` for that. + +## Gotchas found in Phase 2 +- The regfile write-first bypass belongs to the pipeline only; `core_single` instantiates it with `BYPASS=0` (see ARCHITECTURE.md). +- cocotb 2.x: use `COCOTB_TEST_MODULES` (not `MODULE`); never pass `COMPILE_ARGS=` on the make command line (it wipes the + Makefile's `--prefix Vtop`) - use `EXTRA_ARGS`. After `await ReadOnly()` you cannot write a signal: await a `Timer` first. +- A test that calls a helper repeatedly must start the clock once (`ensure_clock` in test_core_lockstep.py). +- Passing tests prove nothing until a mutant fails them: every suite here was mutation-checked; the jalr-bit0 gap was found that way. + +## Gotchas found in Phase 3 +- Pipeline needs a write-first DMEM (`mem_sync` READ_NEW=1): a load in EX reads at the same edge the store ahead of it writes. +- Run a PIPE suite with `TINYCPU_CORE=PIPE` and `-G'CORE="PIPE"'` (sim/run.sh pipe does both); `CORE` is a string parameter. +- Cycle-dependent registers must be masked in the final register compare too, not only per retire. +- Dynamic hazard distance != static: instructions at jump targets skip the jump's padding (use `raw_hazards()` on the ISS trace). +- Hazard-free means >= 3 instructions between producer and consumer (2 NOPs) for this pipeline; `Prog.li/exit/putc` add gap NOPs at pad<2. + +## Gotchas found in Phase 4 +- A taken branch to pc+4 is invisible in `pc_wdata`; `Retire.taken` (ISS) carries it so the timing model stays exact. +- Hand-derived expectations must separate setup from the hazard under test (`lui; lw` is itself a distance-1 RAW). +- A flush hides the stall of the squashed instruction (`jal rd; use rd` costs 2 either way). +- `RANDOM_SEEDS=N bash sim/run.sh pipe` scales the random sweep (default 200 per mode); mutation runs use 40. +- The root filesystem filled during a long mutation sweep (home caches, not this repo): keep `/tmp` and logs small. + +## Gotchas found in Phase 5 +- Hazard-free programs take the same cycles with forwarding on and off (that is what hazard-free means), so they cannot test + the forwarding pin. The fixture set includes one hazard-dense program that must differ, and the generator asserts it. +- A harness that re-implements data memory would be tested against itself. The shadow copy is fed only from retire records + (rvfi_mem_*), and `test.mjs` checks it against the ISS for every store the fixtures make. +- Emscripten's temporaries and Verilator output live under `wasm/build/` (on the project disk); the system disk was full twice + during this project, and `/tmp` writes broke tool output. +- Both models are held in reset while `loadImem`/`loadDmem` run, so an inactive model is never clocked by accident. + +## Gotchas found in Phase 6 +- GNU as accepts `;` as a statement separator (the macro programs use it) and pads `.text` with a zero byte plus `c.nop` (01 00) + when the start is odd. `snippet/text_alignment` pins the padding. +- The linker script's `ALIGN(4)` pads `.data` output; the GNU reference strips those lines before linking (program bytes only). +- A branch target printed as `0xffffffd4` from pc 0 must assemble to offset -44: addresses wrap modulo 2^32. +- `vitest` picks up `*.spec.ts`; `vitest.config.ts` limits it to `src/**/*.test.ts` so the Playwright specs in `e2e/` stay separate. +- A stale `astro dev` process on port 4321 made the Playwright webServer reuse it; stop dev servers first. +- Deviations from PLAN section 8: the editor is a textarea with a line gutter (no CodeMirror); each run keeps its full cycle history + (capped at 20,000) instead of a ring buffer, so stepping backwards is exact. + +## Gotchas found in Phase 7 +- Hazard-case programs must avoid accidental dependencies: the "independent" case first had an `add` one instruction after a + write to `t1`, which made it a distance-1 hazard. The Vitest suite caught it (forwarding on/off cycles differed). +- The datapath view needs operand values, which a pipeline run does not expose per instruction. It reads them from a single-cycle + run of the same program; both cores compute the same architecture, so the values match. +- `tabpanel` ids and `aria-controls` must match the tab ids exactly, or the Playwright role queries and screen readers miss them. diff --git a/README.md b/README.md index 5250e47..091bd28 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,11 @@ Third in the series after [tiny-gpu](https://github.com/deaneeth/tiny-gpu) and [ -> **Status: Phase 0 (scaffold + toolchain spike).** The CPU itself starts in Phase 1-2. +> **Status: Phase 7 done (views).** The app at `/app` has four views: the pipeline, the single-cycle datapath, a hazard lab that measures each hazard on the pipeline with and without forwarding, and a programs comparison with a data-memory heatmap. Next: the docs pages and launch. +> +> *Previous: Phase 6 done (web app, first slice); Phase 5 done (WASM harness).* Both cores run in one WebAssembly module (`wasm/build.sh`, 77 KB gzipped) and pass the golden fixtures in Node (`node wasm/test.mjs`). Next: the web app. +> +> *Previous: Phase 4 done (hazards).* Both RV32I cores retire instructions identically to the Python golden ISS on all 40 official `rv32ui-p-*` tests, 5 programs and hundreds of random programs. The 5-stage core has forwarding and load-use stalls (switchable at runtime) and matches an independent timing model cycle for cycle. Next: the WASM harness. > See [`docs/PLAN.md`](docs/PLAN.md) for the full roadmap. Every pipeline register, forwarding path and stall the UI will draw is a live `dbg_*` signal from the @@ -34,11 +38,11 @@ rtl/*.sv -> Verilator C++ -> Emscripten -> web/public/tiny_cpu.wasm -> React isl | Path | Contents | |---|---| -| `rtl/` | SystemVerilog (`spike/counter.sv` is the Phase 0 toolchain check) | -| `sim/` | cocotb tests, golden ISS (Phase 1) | -| `sw/` | linker script, crt0, riscv-tests port (Phase 1) | +| `rtl/` | SystemVerilog: single-cycle and 5-stage cores + building blocks (`spike/counter.sv` is the Phase 0 toolchain check) | +| `sim/` | golden ISS (`sim/golden/`), cocotb tests, asm programs (`sim/programs/`) | +| `sw/` | linker script, crt0, riscv-tests port + Makefile | | `wasm/` | Verilator -> Emscripten harness | -| `web/` | Astro + React visualizer | +| `web/` | Astro + React app: assembler, editor, pipeline view, cycle controls | | `fpga/` | Yosys/nextpnr flows (Phase 9) | | `docs/` | plan, architecture, state schema | @@ -49,13 +53,13 @@ rtl/*.sv -> Verilator C++ -> Emscripten -> web/public/tiny_cpu.wasm -> React isl python3 -m venv ~/.venvs/tinycpu && source ~/.venvs/tinycpu/bin/activate pip install -r sim/requirements.txt verilator --lint-only -Wall --top-module counter rtl/spike/counter.sv -cd sim && make WAVES=0 +bash scripts/lint.sh && bash sim/run.sh all # needs: make -C sw all -# WASM spike (needs Verilator + Emscripten) -bash wasm/spike/build.sh && node wasm/spike/test.mjs +# WASM (needs Verilator + Emscripten) +bash wasm/build.sh && node wasm/test.mjs # Web -cd web && pnpm install && pnpm dev # open /spike after building the WASM +cd web && pnpm install && pnpm dev # then open /app (or `pnpm build && pnpm test:e2e` for the smoke tests) ``` ## License diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..21f2fa7 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,206 @@ +# TinyCPU architecture + +Decisions that are fixed live in `CONTEXT.md`; the full roadmap is `docs/PLAN.md`. This file describes +what is actually built. + +## Single-cycle core (`rtl/core_single.sv`, Phase 2) + +``` + +--------------------------------------------------------------------------+ + pc_q -> | IMEM (comb) -> decoder -> imm_gen | + | | | + | regfile(rs1,rs2) -> ALU (a: rs1|pc, b: rs2|imm) -> LSU -> mmio/DMEM | + | ^ | \ | | + | | | branch_unit load_value | + | +---- wb mux <-----+--- pc+4 / csr <-------------+ | + +--------------------------------------------------------------------------+ + next_pc = jalr ? {alu_y[31:1],0} : (jal | taken branch) ? pc+imm : pc+4 +``` + +One instruction retires per clock (CPI = 1), so `cycles == instret`; the lockstep test asserts it. + +| Block | File | Notes | +|---|---|---| +| ALU | `alu.sv` | `ALU_COPY_B` implements LUI; shifts use `b[4:0]` | +| Decoder | `decoder.sv` | Legality identical to the ISS; illegal forces every side-effecting control low | +| Immediates | `imm_gen.sv` | I/S/B/U/J selected by the decoder | +| Branches | `branch_unit.sv` | funct3 2 and 3 never take | +| Load/store | `lsu.sv` | Byte lanes, masks, sign/zero extension, forced alignment | +| Address decode + MMIO | `mmio.sv` | DMEM / CONSOLE / TOHOST / CYCLE and the exact fault rules | +| Registers | `regfile.sv` | x0 = 0, reset to 0, `BYPASS` parameter (see below) | +| Counters | `csr_counters.sv` | Zicntr read-only: cycle/time/instret + high halves | +| Memory | `mem_comb.sv` | Combinational read (simulation); `mem_sync.sv` arrives with the pipeline | +| Top | `tiny_cpu_top.sv` | `CORE` parameter, memory loader port, RVFI + debug outputs | + +### Contracts + +* **Retire record (`rvfi_*`)** is *registered*: after a clock edge it describes the instruction that retired in + the cycle that just ended; `rvfi_valid` is high for exactly that cycle. Field conventions match + `sim/golden/iss.py` (word-aligned `mem_addr`, lane-positioned data, `rd_addr = 0` for no write). +* **Faults** (illegal instruction, bus fault, misaligned or out-of-IMEM jump) set `fault`, stop the core, and + retire nothing - no register, memory or MMIO side effect. The ISS raises on exactly the same instructions. +* **Halt** (`ecall`, `ebreak`, TOHOST word store) retires, then sets `halted`. The core is frozen afterwards. +* **Loader**: with `rst_n` low, pulse `load_en` + `load_sel` (0 IMEM, 1 DMEM) + `load_addr` (word) + `load_data`. + +### Decisions and lessons + +* **The regfile write-first bypass must be off in the single-cycle core** (`BYPASS = 0`). The pipelined core + needs it (the writer in WB is a different instruction from the reader in ID), but here the writer *is* the + reader: `lw x1, 0(x1)` would read its own loaded value as the address, which is both wrong and a real + combinational loop (Verilator's UNOPTFLAT lint caught it). The pipeline keeps `BYPASS = 1`. +* **`jalr` clears bit 0 of the target.** No official rv32ui test exercises an odd target, so + `jalr_clears_bit0_of_the_target` in `sim/test_core_lockstep.py` pins it (found by mutation testing). +* `uses_rs1/uses_rs2` are already decoded for the Phase 4 hazard unit; the single-cycle core ignores them. +* `cycle_dependent` retires (`rdcycle`, `rdtime`, the CYCLE MMIO read) are flagged by the RTL and masked by + the lockstep comparison, because the ISS cannot know pipeline timing. + +## 5-stage pipelined core (`rtl/core_pipe.sv`, Phase 3) + +``` + pc_q --> IMEM(sync) ==> [IF/ID] --> decode, regfile read ==> [ID/EX] --> ALU, branch ==> [EX/MEM] --> LSU, MMIO ==> [MEM/WB] --> reg write, retire + ^ addressed id_pc DMEM addressed here store/console/tohost CCSR read + | by pc_q (sync read) effects happen here + +--------------------------- redirect (taken branch / jal / jalr) from EX: flush IF/ID + ID/EX ---------------------------+ +``` + +Real output of `sim/test_pipe_lockstep.py::five_instructions_overlap_and_flushes_match_redirects` (each cell is the pc +in that stage after the clock edge; the `jal` at 0x48 is in EX when it flushes): + +``` +cycle IF ID EX MEM WB + 18 0x0048 0x0044 0x0040 0x003c 0x0038 five instructions in flight + 19 0x004c 0x0048 0x0044 0x0040 0x003c + 20 0x0050 0x004c 0x0048 0x0044 0x0040 <- flush (jal 0x48 -> 0x54) + 21 0x0054 -- -- 0x0048 0x0044 two bubbles = the 2-cycle penalty + 22 0x0058 0x0054 -- -- 0x0048 +``` + +Deviation from the original plan: the taken-branch flush landed in Phase 3, not 4. Without it the NOPs after a taken +branch retire on the wrong path and the lockstep stream can never match the ISS. + +### Hazards (Phase 4) + +| Producer ahead of the consumer | forwarding **on** | forwarding **off** (`cfg_forward_en = 0`) | +|---|---|---| +| ALU / link, distance 1 | forwarded EX/MEM -> EX, **0 stalls** | 2 stalls | +| ALU / link, distance 2 | forwarded MEM/WB -> EX, **0 stalls** | 1 stall | +| load or CSR read, distance 1 | **1 stall** (load-use), then MEM/WB | 2 stalls | +| load or CSR read, distance 2 | **0 stalls** (MEM/WB) | 1 stall | +| any producer, distance >= 3 | register file write-first bypass, 0 | 0 | +| taken branch / jump | flush IF/ID + ID/EX: **2 cycles** (predict not-taken); a flush hides any stall on the squashed instruction | same | + +* `forward_unit` picks `dbg_fwd_a/b` per operand (0 none, 1 EX/MEM, 2 MEM/WB; the nearer producer wins; never x0). +* `hazard_unit` raises `dbg_stall` (hold `pc_q`, the IF/ID register and IMEM's output register via `imem_re = 0`, and put a + bubble in ID/EX) and `dbg_load_use`. A redirect from EX always wins over a stall. +* **Loads and CSR reads are "late" producers.** Load data exists only after MEM, and a CSR is read in WB, so neither can be + forwarded from EX/MEM; their consumers wait one cycle and take the value from MEM/WB. +* **With forwarding off the cost is 2, not 3**, because the register file's write-first bypass delivers a value written + in WB to a reader in ID in the same cycle. (The original plan's "3" assumed no bypass.) +* **Exact timing is specified outside the RTL** by `sim/golden/pipemodel.py`: with `T(i)` the cycle instruction `i` is in + EX, `T(i) >= T(i-1)+1`, `>= T(branch)+3` after a taken branch, `>= T(p)+2` for a late producer / `+1` otherwise with + forwarding, `>= T(p)+3` without. The RTL must match it to the cycle on every program (`cycles = T(last) + 2`). +* Pinned numbers: `sim/golden/hazard_cases.py` (paper-derived, checked against the model *and* the RTL, including the debug + flags) and `sim/perf_baseline.json` (cycles per program, per core and mode; `python -m golden.perf`). + +What the baseline shows: on loop-heavy code CPI with forwarding is 1.4-1.65, and almost all of the excess is the 2-cycle +penalty on every taken loop branch (predict-not-taken), not data hazards - the case for a branch predictor (PLAN stretch #2). + +### Timing contract +* **Synchronous memories.** `pc_q` is the IMEM address; the BRAM output register *is* the instruction in ID, with `id_pc` + carried beside it. DMEM is addressed in EX, so load data is back in MEM, where the LSU extracts and extends it. + The IF stage therefore has no instruction word to show (`dbg_stage_insn[IF] = 0`; look it up from the pc). +* **Write-first DMEM** (`mem_sync` `READ_NEW=1`). A load reads DMEM in EX at the same clock edge at which the store + ahead of it writes in MEM; without write-first the load returns stale data. This is the memory twin of a forwarding + path, and `mem_raw_adjacent` pins it (the only test that fails when it is removed). +* **Cycle count.** First retire is visible after 5 edges, then one per cycle: `cycles = instret + 4 + 2 * redirects`. + The lockstep test asserts this exactly, and the flush flag fires once per redirect. +* **CSR read in WB.** `instret` seen by an instruction equals the number retired before it, as in the ISS. + `dbg_pc` is the *architectural* pc (next instruction to retire), not the fetch pc. + +### Precise faults and halt +A faulting or halting instruction travels to WB like any other. While it sits there (`older_stop`) younger +instructions in MEM are gated from writing DMEM, CONSOLE or TOHOST; once it leaves, `active` freezes the core. +Faulting instructions never retire; nothing younger ever retires. `younger_instructions_have_no_effect_after_*` +runs dense code with stores directly behind the faulting/halting instruction and checks that nothing leaks. + +### Lessons +* `lockstep` must mask registers last written by timing-dependent reads (rdcycle, CYCLE MMIO) in the *final* + register compare as well as per-retire; the single-cycle core hid this because there cycle == instret. +* A taken jump flushes its own padding NOPs, so the instruction at a jump *target* is only 1 instruction behind the + jump's link register. `Prog` pads after instructions, not at labels: the dynamic `raw_hazards()` check found this. + +## Verification (Phase 2 and 3) + +| Layer | Where | What | +|---|---|---| +| Unit | `sim/test_*.py` | Every block vs the golden model / independent tables; decoder legality vs the ISS on 4000+ words and every single-bit mutation of each opcode | +| Constants | `sim/golden/test_consts.py` | `sim/consts.py` == `rtl/tiny_cpu_pkg.sv` == `golden/iss.py` | +| Lockstep | `sim/test_core_lockstep.py` | 45 images (40 riscv-tests + 5 programs) retire-for-retire against the ISS; 13 fault cases; halt/frozen-core checks; CPI = 1 | +| Lint | `scripts/lint.sh` | `-Wall` clean for every module standalone and the whole design | + +| Pipeline lockstep | `sim/test_pipe_lockstep.py` | In **both forwarding modes**: the 40 riscv-tests + 5 programs, 9 hazard-free programs, 60 hazard-free and 200 hazard-dense random programs (`RANDOM_SEEDS=N` scales it; nightly runs 5000) retire-for-retire vs the ISS; 13 fault cases; younger-instruction gating after fault/halt; cycle count == the independent model; 5-deep overlap | +| Hazard patterns | `sim/test_pipe_hazards.py` | 17 paper-derived patterns x 2 modes: pinned extra cycles, `dbg_stall` count == model, one `dbg_load_use` pulse where expected, forwarding source observed | +| Hazard units | `sim/test_forward_unit.py`, `test_hazard_unit.py` | Exhaustive over (control bits x small register space): 5184 and 20736 combinations | +| Timing model | `sim/golden/test_pipemodel.py` | Paper-derived cases + random-program sanity, no RTL | +| Program builder | `sim/golden/progbuild.py`, `hazard_free.py` | `Prog(pad=N)` mini-assembler; `raw_hazards()` proves a program hazard-free from its dynamic ISS trace (no toolchain needed) | + +Run: `bash scripts/lint.sh && bash sim/run.sh all` (after `make -C sw all`). `all` = unit + lockstep (single-cycle) + pipe. + + +## WASM harness (Phase 5) + +`wasm/harness.cpp` Verilates `tiny_cpu_top` twice - `CORE="SINGLE"` and `CORE="PIPE"` - with distinct `--prefix`, and links +both into one module. `TinyCpuSim` owns both models and `reset(core)` selects one; program memory is written to both, and +both are held in reset while loading, so the inactive core is never clocked by accident. + +Every value comes from an output port of the Verilated model. The one exception is data memory, which the RTL does not +expose: the harness keeps a shadow copy fed from the retire records (`rvfi_mem_*`). The JavaScript contract is +`docs/STATE_SCHEMA.md`. + +Verification (`node wasm/test.mjs`, 534 checks): each fixture in `wasm/test/fixtures.json` runs on SINGLE and on PIPE with +forwarding on and off. It checks console output, final registers (timing-dependent ones excluded), data memory, instruction +count, the SHA-256 of the retire stream against the Python ISS, the halt/tohost behaviour, and the cycle count against the +pipeline model. Four mutations were caught: a forwarding pin ignored, a console byte dropped, a cycle counter off by one, and +the shadow memory's byte order reversed. + +Size: 194 KB raw, 77 KB gzipped (budget 400 KB), enforced by `build.sh`. + + +## Web app (Phase 6) + +`/app` is one React island (`CpuApp`) over `useTinyCpu`: + +* **Assembler** (`web/src/lib/assembler.ts`): two passes; labels, numeric local labels, `.equ`, the data directives and the + GNU pseudo-instructions. Output is byte-identical to GNU as/ld for every source in `gnu-asm.json` (the presets, the five macro + programs preprocessed, and 16 snippets covering every mnemonic and directive). +* **Run**: one `TinyCpuSim` per run. The full `CycleState[]` is kept (capped at 20,000 cycles), so stepping, scrubbing and + playback only move an index; nothing re-runs. `run-model.ts` derives statistics, console output and data memory at any cycle + from the states themselves (memory by replaying retire records, the same rule as the harness's shadow copy). +* **Views**: source editor with the executing line highlighted and errors marked; a pipeline view (five stage cards, hazard + badges, and a stage timeline over the last ten cycles); registers and data memory with changed values highlighted; console; + statistics. Single-cycle runs show one instruction per cycle; hazard badges appear only for the pipeline. +* **Checks**: `web/src/lib/__tests__/` (Vitest, 53 tests, including every preset on both cores with forwarding on and off, read + against the expected console output) and `web/e2e/` (Playwright, 4 tests against the production build). + +## Views (Phase 7) + +The `/app` page has four views selected by `LevelTabs`: + +* **L2 Pipeline** (default): stage cards, hazard badges, and the stage timeline (as in Phase 6). +* **L1 Datapath** (`DatapathView`, SVG, fixed 900x430 coordinates): highlights the wires the current retired instruction uses and + labels them with the values read from the register file and the retire record. `datapath.ts` derives those values from a + single-cycle run of the same program: operands come from the register file before the instruction, results from its retire + record. A pipeline run does not expose per-instruction operands, so the single-cycle run supplies them; the architecture is + identical on both cores. +* **L3 Hazard lab** (`HazardLab`, `hazard-lab.ts`): eight short programs, one per hazard kind (independent, ALU distance 1 and 2, + load-use, store of a loaded value, store then load, taken and not-taken branches). Each runs on the pipeline with forwarding on, + the pipeline with forwarding off, and the single-cycle core. The table shows cycles, stalls, flushes and the difference from + single-cycle; the timelines show the first eight cycles. +* **L4 Programs** (`ProgramsView`, `programs-compare.ts`): every preset on the single-cycle core and the pipeline, with CPI and + PASS/FAIL, plus a data-memory heatmap of loads and stores per word (`accessCounts` in `run-model.ts`). + +Tests: `web/src/lib/__tests__/phase7.test.ts` (67 Vitest tests in the suite now) runs these against the WASM module, and +`web/e2e/app.spec.ts` (9 Playwright tests) covers the tabs, the hazard lab, the datapath, the programs view, phone width with no +horizontal scroll, and the theme toggle. + +Clock speed is not modelled: the cycle counts are what the programs view reports, and Phase 9's FPGA numbers will turn them into time. diff --git a/docs/PLAN.md b/docs/PLAN.md index 25e5e7c..9fc5035 100644 --- a/docs/PLAN.md +++ b/docs/PLAN.md @@ -67,7 +67,7 @@ fits iCE40 BRAM. | Memory read timing | **Synchronous** read on both IMEM and DMEM | Maps straight to FPGA BRAM. IMEM is addressed with `pc_next`, so the instruction is ready in IF/ID. DMEM is addressed in EX, so data is ready in MEM. | | Branch / JAL / JALR resolution | In **EX**, predict not-taken | Simplest correct design. A taken branch flushes IF/ID and ID/EX (2-cycle penalty), which is very visible in the UI. | | Forwarding | EX/MEM→EX and MEM/WB→EX, for both rs1 and rs2 | Standard design. Store data (rs2) is forwarded as well. | -| Register-file write/read in the same cycle | Write-first bypass inside `regfile.sv` | Removes the WB→ID hazard without an extra forwarding path. | +| Register-file write/read in the same cycle | Write-first bypass inside `regfile.sv`, parameter `BYPASS` (1 in the pipeline, **0 in the single-cycle core**) | Removes the WB→ID hazard without an extra forwarding path. In the single-cycle core the writer is the reader, so a bypass is wrong and a combinational loop (found in Phase 2). | | Load-use hazard | 1-cycle stall: freeze PC and IF/ID, insert a bubble into ID/EX | The only stall in v1. | | `x0` | Hard-wired to zero in the register file. Forwarding ignores `rd == 0` | A common bug source; covered by tests. | | Runtime knob `cfg_forward_en` | Top-level input pin. When 0, forwarding is disabled and the hazard unit stalls on **every** RAW hazard | Lets the Hazard Lab toggle forwarding live with one WASM build. | @@ -91,6 +91,8 @@ rvfi_mem_addr, rvfi_mem_rmask, rvfi_mem_wmask, rvfi_mem_rdata, rvfi_mem_wdata, rvfi_halt ``` +Phase 1 additions (implemented in `sim/golden/iss.py`): `pc_wdata` (next PC), `misaligned` (load/store was forced into alignment) and `cycle_dependent` (rd value depends on timing: `rdcycle`/`rdtime`/CYCLE MMIO; lockstep masks it). `mem_addr` is the word-aligned address, masks are byte lanes within it, and `mem_rdata`/`mem_wdata` are lane-positioned words. + --- ## 3. Repository layout (mirrors tiny-tpu) @@ -197,15 +199,15 @@ Four layers. Each phase below says which layers must be green. readable diff with the disassembly of the last 8 retired instructions. 3. **Official compliance:** all `rv32ui-p-*` tests from `riscv-tests` on **both** cores, using a custom `env/tinycpu` (our `link.ld`, with `RVTEST_PASS/FAIL` writing `TOHOST`). - Excluded: `fence_i`, `ma_data`. Expected: 37–39 tests pass, depending on the riscv-tests revision. + Excluded: `fence_i`, `ma_data`. Verified in Phase 1: 40 tests (42 sources minus those two) on riscv-tests `3df703c`; the largest, `ld_st`, is 3732 B and fits the 4 KiB IMEM. 4. **Random programs:** `test_random.py` generates N programs (seeded) of 200–2000 instructions, weighted toward hazards: back-to-back RAW, load-use, branch after load, store after load, writes to x0, JALR to computed targets. Every program runs in lockstep, with `cfg_forward_en` ∈ {0, 1}. CI runs 200 seeds; nightly runs 5000. **Directed hazard tests** (`sim/programs/hazards_*.S` + `test_hazards.py`) check **exact -cycle counts**, not just results. Example: `lw x1,0(x2); add x3,x1,x1` must take exactly 1 -extra cycle with forwarding on and 3 with it off. These numbers are the ones the Hazard Lab +cycle counts**, not just results. Example: `lw x1,0(x2); add x3,x1,x1` takes exactly 1 +extra cycle with forwarding on and 2 with it off (Phase 4 measured: the write-first regfile bypass saves the third). These numbers are the ones the Hazard Lab displays, so they get pinned. **Performance assertions:** fib(20) and bubble sort on 32 elements have pinned cycle counts per @@ -357,7 +359,7 @@ Estimates assume one person working part-time, roughly at the pace of tiny-tpu. ### Phase 3: Pipelined core without hazard handling (1 week) - `mem_sync`, `core_pipe` with IF/ID/EX/MEM/WB registers (packed structs), `dbg_*` ports per stage. -- Hazards are handled by hand-inserted NOPs only. A `hazard_free/*.S` set proves the datapath works. +- Data hazards are avoided by construction (NOP padding via `golden/progbuild.py`); `hazard_free.py` holds the program set. The taken-branch flush is included here because lockstep cannot match the ISS without it. - **Exit:** the hazard-free programs pass lockstep; the waveform in GTKWave shows the 5-stage overlap. ### Phase 4: Hazards (1.5 weeks) diff --git a/docs/STATE_SCHEMA.md b/docs/STATE_SCHEMA.md new file mode 100644 index 0000000..1050017 --- /dev/null +++ b/docs/STATE_SCHEMA.md @@ -0,0 +1,110 @@ +# STATE_SCHEMA.md - the WASM per-cycle contract + +The JavaScript API of `wasm/harness.cpp`, as shipped in `web/public/tiny_cpu.mjs`. Every value is read from an +output port of the Verilated model - nothing is re-computed in C++ or JavaScript - except data memory, which is +kept as a shadow copy from the retire records (see below). `wasm/test.mjs` checks this contract. + +## Loading and running + +```js +const { default: createTinyCpu } = await import("/tiny_cpu.mjs"); +const mod = await createTinyCpu(); +const sim = new mod.TinyCpuSim(); // owns BOTH cores; one instance is one simulation +sim.setForwarding(true); // PIPE only; takes effect at the next reset() +sim.reset("PIPE"); // "SINGLE" or "PIPE"; false for any other name +sim.loadImem(words); // array of uint32, at most 1024 words +sim.loadDmem(bytes); // array of bytes, at most 4096, little-endian +sim.start(); // release reset; cycle 1 is the next step +const states = sim.run(200000); // every CycleState up to halt/fault (the last one is the halt) +sim.delete(); +``` + +| Method | Returns | Notes | +|---|---|---| +| `reset(core)` | `boolean` | Selects the core, holds reset on both. Clears console and cycle count. | +| `setForwarding(on)` | - | `cfg_forward_en` pin. Ignored by SINGLE (it has no hazards). | +| `loadImem(words)` / `loadDmem(bytes)` | `boolean` | `false` if too large. Both cores are held in reset while loading, and the whole memory is written (zero-padded), so a previous program never leaks. | +| `start()` | - | Releases reset. The next `step()` is cycle 1. | +| `step()` | CycleState \| `null` | One clock edge. `null` before `start()`. After halt or fault it returns the same state without advancing. | +| `run(maxCycles)` | CycleState[] | Steps until halt, fault or the limit. | +| `runFast(maxCycles)` | summary | `{cycles, instret, halted, fault, tohost, console}` without per-cycle states. | +| `getRegs()` | `number[32]` | Architectural registers from the register-file port. | +| `readDmem(addr, len)` | `number[]` | Data memory at absolute address `addr` (base `0x0001_0000`). | +| `getConsole()` | `string` | Bytes written to CONSOLE since `reset()`. | +| `getCycleCount()`, `isHalted()`, `isFault()` | number / boolean | | + +## CycleState + +Returned by `step()` and `run()`. Each value is the state **after** the clock edge that produced it. + +```ts +interface CycleState { + cycle: number; // clock edges since start(); 1 for the first step + halted: boolean; // the halting instruction has retired (ecall, ebreak or a TOHOST store) + fault: boolean; // illegal instruction, bus fault or misaligned jump; nothing retired + stages: Stage[5]; // index 0..4 = IF, ID, EX, MEM, WB + hazard: Hazard; + regs: number[32]; // x0..x31 from the register file + retired: Retire | null; // the instruction that retired on this edge, if any + console: number | null; // byte written to CONSOLE on this edge, else null + tohost: number | null; // TOHOST value once written (sticky), else null + counters: { cycle: number; instret: number }; +} + +interface Stage { + name: "IF" | "ID" | "EX" | "MEM" | "WB"; + valid: boolean; // a real instruction (false = bubble) + pc: number; // pc of the instruction in this stage + insn: number; // 32-bit instruction word; 0 for IF on PIPE (fetch is synchronous) +} + +interface Hazard { + stall: boolean; // ID is stalled this edge + loadUse: boolean; // the stall is a load-use stall (PIPE with forwarding) + flushIFID: boolean; // a taken branch or jump squashed IF/ID this edge + flushIDEX: boolean; // ... and ID/EX + fwdA: 0 | 1 | 2; // operand A forwarded from: 0 none, 1 EX/MEM, 2 MEM/WB + fwdB: 0 | 1 | 2; +} + +interface Retire { // RVFI-lite, docs/PLAN.md section 2.4 (field meanings match sim/golden/iss.py) + order: number; // index of this instruction in the retire stream, from 0 + pc: number; insn: number; + rdAddr: number; rdWdata: number; // rdAddr 0 = no register written + pcWdata: number; // address of the next instruction + memAddr: number; // word-aligned; 0 if no memory access + memRmask: number; memWmask: number; // byte lanes (4 bits) + memRdata: number; memWdata: number; // lane-positioned 32-bit words + misaligned: boolean; // a load/store that was forced into alignment + halt: boolean; + cycleDep: boolean; // value depends on timing (rdcycle/rdtime, CYCLE MMIO) +} +``` + +### Semantics the UI relies on + +* **Cycle numbering.** `cycle` counts clock edges from `start()`. The halting instruction is visible on the edge + that makes `halted` true, so a run's length in cycles is `run().length`. SINGLE: exactly `instret` cycles. PIPE: + the value from the timing model `golden/pipemodel.py`, which the RTL suites check cycle for cycle. +* **Stages.** Stage index 0 is IF through index 4 is WB. On SINGLE only IF is populated, and it shows the instruction + being executed, so SINGLE's `stages[0].insn` is the real word. +* **Console and TOHOST.** `console` is the byte written on that edge; `tohost` stays set after the write. + A PASS run has `tohost === 1`. +* **Timing-dependent values.** A register last written by a `cycleDep` retire holds a cycle count. Its value + differs between SINGLE and PIPE and from the ISS. Compare those with care. +* **States after halt or fault.** No instruction retires and no memory changes; the state is frozen. + +## Data memory + +The RTL keeps data memory inside its memories, so the harness keeps a shadow copy. It is updated from the retire +records (`memWmask` lanes and `memWdata`) for stores into DMEM, and from `loadDmem`. Stores to CONSOLE and TOHOST +are excluded by address. Stores that have not yet retired are not visible, which is the architectural view a +program sees at a halt. Loads never change memory, so the shadow is exact. + +## Sync requirements + +This document and the ISS (`sim/golden/iss.py`), the timing model (`sim/golden/pipemodel.py`) and the fixtures +(`wasm/test/fixtures.json`, generated by `sim/golden/wasm_fixtures.py`) must agree. `sim/golden/test_wasm_fixtures.py` +checks the fixtures; `wasm/test.mjs` checks the built module against them. + +The TypeScript mirror for the web app (`web/src/lib/state-schema.ts`) lands in Phase 6. diff --git a/rtl/alu.sv b/rtl/alu.sv new file mode 100644 index 0000000..de16f0e --- /dev/null +++ b/rtl/alu.sv @@ -0,0 +1,31 @@ +// Combinational ALU shared by the single-cycle and pipelined cores. +module alu + import tiny_cpu_pkg::*; +( + input logic [31:0] a, + input logic [31:0] b, + input alu_op_t op, + output logic [31:0] y +); + + logic [4:0] shamt; + assign shamt = b[4:0]; + + always_comb begin + case (op) + ALU_ADD: y = a + b; + ALU_SUB: y = a - b; + ALU_SLL: y = a << shamt; + ALU_SLT: y = {31'b0, $signed(a) < $signed(b)}; + ALU_SLTU: y = {31'b0, a < b}; + ALU_XOR: y = a ^ b; + ALU_SRL: y = a >> shamt; + ALU_SRA: y = 32'($signed(a) >>> shamt); + ALU_OR: y = a | b; + ALU_AND: y = a & b; + ALU_COPY_B: y = b; + default: y = 32'b0; + endcase + end + +endmodule diff --git a/rtl/branch_unit.sv b/rtl/branch_unit.sv new file mode 100644 index 0000000..2809429 --- /dev/null +++ b/rtl/branch_unit.sv @@ -0,0 +1,22 @@ +// Conditional-branch comparator. funct3 is insn[14:12]; the reserved encodings 2 and 3 never +// take (the decoder flags them illegal). +module branch_unit ( + input logic [31:0] a, + input logic [31:0] b, + input logic [2:0] funct3, + output logic taken +); + + always_comb begin + case (funct3) + 3'b000: taken = (a == b); // beq + 3'b001: taken = (a != b); // bne + 3'b100: taken = ($signed(a) < $signed(b)); // blt + 3'b101: taken = ($signed(a) >= $signed(b)); // bge + 3'b110: taken = (a < b); // bltu + 3'b111: taken = (a >= b); // bgeu + default: taken = 1'b0; + endcase + end + +endmodule diff --git a/rtl/core_pipe.sv b/rtl/core_pipe.sv new file mode 100644 index 0000000..e8fad82 --- /dev/null +++ b/rtl/core_pipe.sv @@ -0,0 +1,422 @@ +// 5-stage pipelined RV32I core: IF -> ID -> EX -> MEM -> WB. +// +// Hazards (Phase 4): forward_unit forwards EX/MEM and MEM/WB results into EX; hazard_unit stalls +// ID for the load-use hazard (loads and CSR reads have their result only in WB). The pin +// cfg_forward_en = 0 disables forwarding and makes hazard_unit stall on every RAW dependency until +// the producer reaches WB (the register file's write-first bypass delivers it). Exact cycle counts +// are specified by the independent model in sim/golden/pipemodel.py. +// +// Timing (docs/ARCHITECTURE.md): +// IF pc_q addresses IMEM (synchronous read). The instruction word is the BRAM output register +// and is decoded in ID the next cycle, alongside id_pc. A stall holds pc_q and IMEM's output. +// ID decode, register read (write-first bypass from WB), immediate. Stalls here. +// EX operand forwarding, ALU, branch decision, jump target. Taken branches/jumps (predict +// not-taken) flush IF/ID and ID/EX: a 2-cycle penalty. DMEM is ADDRESSED here. +// MEM load data is back from DMEM; LSU extracts/extends. Stores, console and TOHOST write here. +// WB register write, CSR read, retire (RVFI), halt/fault decision. +// +// Precise faults/halt: a faulting or halting instruction travels to WB like any other. Younger +// instructions are stopped from touching state by gating MEM side effects while an older +// faulting/halting instruction sits in WB, and by freezing the core (`active`) afterwards. Nothing +// younger ever retires. Faulting instructions never retire; the ISS raises on the same ones. +module core_pipe + import tiny_cpu_pkg::*; +( + input logic clk, + input logic rst_n, + input logic cfg_forward_en, // 1: forward + load-use stall; 0: stall on every RAW + + output logic imem_re, + output logic [IMEM_AW-1:0] imem_raddr, + input logic [31:0] imem_rdata, + + output logic [DMEM_AW-1:0] dmem_raddr, + input logic [31:0] dmem_rdata, + output logic [3:0] dmem_wmask, + output logic [DMEM_AW-1:0] dmem_waddr, + output logic [31:0] dmem_wdata, + + output logic rvfi_valid, + output logic [31:0] rvfi_order, + output logic [31:0] rvfi_pc, + output logic [31:0] rvfi_insn, + output logic [4:0] rvfi_rd_addr, + output logic [31:0] rvfi_rd_wdata, + output logic [31:0] rvfi_pc_wdata, + output logic [31:0] rvfi_mem_addr, + output logic [3:0] rvfi_mem_rmask, + output logic [3:0] rvfi_mem_wmask, + output logic [31:0] rvfi_mem_rdata, + output logic [31:0] rvfi_mem_wdata, + output logic rvfi_misaligned, + output logic rvfi_halt, + output logic rvfi_cycle_dep, + + output logic console_valid, + output logic [7:0] console_data, + output logic tohost_valid, + output logic [31:0] tohost_data, + + output logic halted, + output logic fault, + output logic [31:0] dbg_pc, // architectural pc: next instruction to retire + output logic [32*32-1:0] dbg_regs, + output logic [31:0] dbg_cycle, + output logic [31:0] dbg_instret, + + output logic [4:0] dbg_stage_valid, // [0]=IF [1]=ID [2]=EX [3]=MEM [4]=WB + output logic [5*32-1:0] dbg_stage_pc, + output logic [5*32-1:0] dbg_stage_insn, + output logic dbg_flush_ifid, + output logic dbg_flush_idex, + output logic dbg_stall, + output logic dbg_load_use, + output logic [1:0] dbg_fwd_a, // 0 none, 1 EX/MEM, 2 MEM/WB + output logic [1:0] dbg_fwd_b +); + + // ====================================================================== state + logic [31:0] pc_q; + logic halted_q, fault_q, active; + logic [31:0] arch_pc_q; + + assign active = !halted_q && !fault_q; + + // IF/ID + logic id_valid, id_fetch_fault; + logic [31:0] id_pc; + + // ID/EX + logic ex_valid, ex_illegal, ex_a_pc, ex_b_imm, ex_reg_we, ex_mem_re, ex_mem_we; + logic ex_mem_unsigned, ex_is_branch, ex_is_jal, ex_is_jalr, ex_is_halt; + logic ex_uses_rs1, ex_uses_rs2; + logic [31:0] ex_pc, ex_insn, ex_rs1_data, ex_rs2_data, ex_imm; + alu_op_t ex_alu_op; + wb_sel_t ex_wb_sel; + mem_size_t ex_mem_size; + + // EX/MEM + logic mm_valid, mm_fault, mm_reg_we, mm_mem_re, mm_mem_we, mm_mem_unsigned, mm_is_halt; + logic [31:0] mm_pc, mm_insn, mm_val, mm_rs2_data, mm_next_pc; + wb_sel_t mm_wb_sel; + mem_size_t mm_mem_size; + + // MEM/WB + logic wb_valid, wb_fault, wb_reg_we, wb_halt, wb_misaligned, wb_cycle_dep; + logic [31:0] wb_pc, wb_insn, wb_val, wb_next_pc; + logic [31:0] wb_mem_addr, wb_mem_rdata, wb_mem_wdata; + logic [3:0] wb_mem_rmask, wb_mem_wmask; + wb_sel_t wb_sel; + + // ====================================================================== IF + logic if_fault; + + logic stall; // from hazard_unit (declared with ID) + assign imem_re = !stall; // hold the instruction word in ID while stalled + assign imem_raddr = pc_q[IMEM_AW+1:2]; + assign if_fault = |pc_q[31:IMEM_AW+2]; // pc outside IMEM + + // ====================================================================== ID + logic [31:0] id_insn, id_imm, id_rs1_data, id_rs2_data; + alu_op_t id_alu_op; + imm_sel_t id_imm_sel; + wb_sel_t id_wb_sel; + mem_size_t id_mem_size; + logic id_a_pc, id_b_imm, id_reg_we, id_mem_re, id_mem_we, id_mem_unsigned; + logic id_is_branch, id_is_jal, id_is_jalr, id_is_halt, id_illegal; + logic id_uses_rs1, id_uses_rs2; + + assign id_insn = id_fetch_fault ? 32'b0 : imem_rdata; + + decoder u_decoder ( + .insn(id_insn), .alu_op(id_alu_op), .a_pc(id_a_pc), .b_imm(id_b_imm), .imm_sel(id_imm_sel), + .reg_we(id_reg_we), .wb_sel(id_wb_sel), .mem_re(id_mem_re), .mem_we(id_mem_we), + .mem_size(id_mem_size), .mem_unsigned(id_mem_unsigned), .is_branch(id_is_branch), + .is_jal(id_is_jal), .is_jalr(id_is_jalr), .uses_rs1(id_uses_rs1), .uses_rs2(id_uses_rs2), + .is_halt(id_is_halt), .illegal(id_illegal) + ); + + imm_gen u_imm_gen (.insn(id_insn), .sel(id_imm_sel), .imm(id_imm)); + + // WB-stage write port (wr_en and wb_wdata are declared with the forwarding logic in EX) + logic [4:0] wb_rd; + + regfile #(.BYPASS(1'b1)) u_regfile ( + .clk(clk), .rst_n(rst_n), .raddr1(id_insn[19:15]), .raddr2(id_insn[24:20]), + .rdata1(id_rs1_data), .rdata2(id_rs2_data), .we(wr_en), .waddr(wb_rd), .wdata(wb_wdata), + .dbg_regs(dbg_regs) + ); + + // ====================================================================== EX + logic [31:0] ex_alu_a, ex_alu_b, ex_alu_y, ex_pc_plus4, ex_pc_target, ex_next_pc, ex_val; + logic ex_br_taken, ex_redirect_raw, ex_target_misaligned, ex_redirect, ex_fault; + + // ---- forwarding (EX/MEM and MEM/WB -> EX) and hazard control (ID stalls) + logic [1:0] fwd_a, fwd_b; + logic [31:0] ex_rs1_v, ex_rs2_v; // operands after forwarding + logic mm_wr, ex_wr, ex_late, load_use; + logic [31:0] wb_wdata; // value being written back (declared with WB) + logic wr_en; + + assign mm_wr = mm_valid && mm_reg_we && (mm_insn[11:7] != 5'd0); + assign ex_wr = ex_valid && ex_reg_we && (ex_insn[11:7] != 5'd0); + assign ex_late = ex_mem_re || (ex_wb_sel == WB_CSR); // result only exists in WB + + forward_unit u_fwd ( + .forward_en(cfg_forward_en), .ex_valid(ex_valid), .ex_uses_rs1(ex_uses_rs1), + .ex_uses_rs2(ex_uses_rs2), .ex_rs1(ex_insn[19:15]), .ex_rs2(ex_insn[24:20]), + .mm_wr(mm_wr), .mm_rd(mm_insn[11:7]), .wb_wr(wr_en), .wb_rd(wb_rd), + .fwd_a(fwd_a), .fwd_b(fwd_b) + ); + + always_comb begin + case (fwd_a) + 2'd1: ex_rs1_v = mm_val; + 2'd2: ex_rs1_v = wb_wdata; + default: ex_rs1_v = ex_rs1_data; + endcase + case (fwd_b) + 2'd1: ex_rs2_v = mm_val; + 2'd2: ex_rs2_v = wb_wdata; + default: ex_rs2_v = ex_rs2_data; + endcase + end + + hazard_unit u_hazard ( + .forward_en(cfg_forward_en), .redirect(ex_redirect), .id_valid(id_valid), + .id_uses_rs1(id_uses_rs1), .id_uses_rs2(id_uses_rs2), .id_rs1(id_insn[19:15]), + .id_rs2(id_insn[24:20]), .ex_wr(ex_wr), .ex_late(ex_late), .ex_rd(ex_insn[11:7]), + .mm_wr(mm_wr), .mm_rd(mm_insn[11:7]), .stall(stall), .load_use(load_use) + ); + + assign ex_alu_a = ex_a_pc ? ex_pc : ex_rs1_v; + assign ex_alu_b = ex_b_imm ? ex_imm : ex_rs2_v; + + alu u_alu (.a(ex_alu_a), .b(ex_alu_b), .op(ex_alu_op), .y(ex_alu_y)); + branch_unit u_branch (.a(ex_rs1_v), .b(ex_rs2_v), .funct3(ex_insn[14:12]), .taken(ex_br_taken)); + + assign ex_pc_plus4 = ex_pc + 32'd4; + assign ex_pc_target = ex_pc + ex_imm; + assign ex_redirect_raw = ex_valid && !ex_illegal && + (ex_is_jal || ex_is_jalr || (ex_is_branch && ex_br_taken)); + always_comb begin + if (ex_is_jalr) ex_next_pc = {ex_alu_y[31:1], 1'b0}; + else if (ex_redirect_raw) ex_next_pc = ex_pc_target; + else ex_next_pc = ex_pc_plus4; + end + assign ex_target_misaligned = ex_redirect_raw && (|ex_next_pc[1:0]); + assign ex_redirect = ex_redirect_raw && !ex_target_misaligned; // taken: flush IF/ID + ID/EX + assign ex_fault = ex_illegal || ex_target_misaligned; + assign ex_val = (ex_wb_sel == WB_PC4) ? ex_pc_plus4 : ex_alu_y; + + // DMEM is addressed in EX; its output register is the load data in MEM. + assign dmem_raddr = ex_alu_y[DMEM_AW+1:2]; + + // ====================================================================== MEM + logic [31:0] lsu_word_addr, lsu_wdata, lsu_rdata_lanes, lsu_load_value, mm_word; + logic [3:0] lsu_lane_mask; + logic lsu_misaligned, hit_dmem, mmio_fault, mmio_console_we, mmio_tohost_we; + logic [31:0] mmio_rdata, mmio_tohost_data, cycle_lo, instret_lo; + logic [7:0] mmio_console_data; + logic mm_fault_total, mem_effects_ok, older_stop, mm_mem_op; + + lsu u_lsu ( + .addr(mm_val), .size(mm_mem_size), .is_unsigned(mm_mem_unsigned), .store_data(mm_rs2_data), + .mem_word(mm_word), .word_addr(lsu_word_addr), .lane_mask(lsu_lane_mask), + .wdata(lsu_wdata), .rdata_lanes(lsu_rdata_lanes), .load_value(lsu_load_value), + .misaligned(lsu_misaligned) + ); + + mmio u_mmio ( + .word_addr(lsu_word_addr), .re(mm_valid && !mm_fault && mm_mem_re), + .we(mm_valid && !mm_fault && mm_mem_we), .wmask(lsu_lane_mask), .wdata(lsu_wdata), + .cycle(cycle_lo), .hit_dmem(hit_dmem), .mmio_rdata(mmio_rdata), + .console_we(mmio_console_we), .console_data(mmio_console_data), + .tohost_we(mmio_tohost_we), .tohost_data(mmio_tohost_data), .fault(mmio_fault) + ); + + assign mm_word = hit_dmem ? dmem_rdata : mmio_rdata; + assign mm_mem_op = mm_mem_re || mm_mem_we; + assign mm_fault_total = mm_valid && (mm_fault || mmio_fault); + + // An older instruction in WB that faults or halts must stop everything younger from having + // any effect (the ISS never executes those instructions). + assign older_stop = wb_valid && (wb_fault || wb_halt); + assign mem_effects_ok = mm_valid && !mm_fault_total && active && !older_stop; + + assign dmem_wmask = (mem_effects_ok && mm_mem_we && hit_dmem) ? lsu_lane_mask : 4'b0000; + assign dmem_waddr = lsu_word_addr[DMEM_AW+1:2]; + assign dmem_wdata = lsu_wdata; + + // ====================================================================== WB + logic [31:0] csr_rdata; + logic retire_now, fault_now, halt_now; + + csr_counters u_csr ( + .clk(clk), .rst_n(rst_n), .retire(retire_now), .csr_addr(wb_insn[31:20]), + .csr_rdata(csr_rdata), .cycle_lo(cycle_lo), .instret_lo(instret_lo) + ); + + assign retire_now = wb_valid && active && !wb_fault; + assign fault_now = wb_valid && active && wb_fault; + assign halt_now = retire_now && wb_halt; + assign wb_rd = wb_insn[11:7]; + assign wb_wdata = (wb_sel == WB_CSR) ? csr_rdata : wb_val; + assign wr_en = retire_now && wb_reg_we && (wb_rd != 5'd0); + + // ====================================================================== pipeline registers + always_ff @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + pc_q <= IMEM_BASE; + id_valid <= 1'b0; id_pc <= 32'b0; id_fetch_fault <= 1'b0; + + ex_valid <= 1'b0; ex_illegal <= 1'b0; ex_pc <= 32'b0; ex_insn <= 32'b0; + ex_rs1_data <= 32'b0; ex_rs2_data <= 32'b0; ex_imm <= 32'b0; + ex_alu_op <= ALU_ADD; ex_a_pc <= 1'b0; ex_b_imm <= 1'b0; ex_reg_we <= 1'b0; + ex_wb_sel <= WB_ALU; ex_mem_re <= 1'b0; ex_mem_we <= 1'b0; ex_mem_size <= SZ_WORD; + ex_mem_unsigned <= 1'b0; ex_is_branch <= 1'b0; ex_is_jal <= 1'b0; + ex_is_jalr <= 1'b0; ex_is_halt <= 1'b0; ex_uses_rs1 <= 1'b0; ex_uses_rs2 <= 1'b0; + + mm_valid <= 1'b0; mm_fault <= 1'b0; mm_reg_we <= 1'b0; mm_mem_re <= 1'b0; + mm_mem_we <= 1'b0; mm_mem_unsigned <= 1'b0; mm_is_halt <= 1'b0; + mm_pc <= 32'b0; mm_insn <= 32'b0; mm_val <= 32'b0; mm_rs2_data <= 32'b0; + mm_next_pc <= 32'b0; mm_wb_sel <= WB_ALU; mm_mem_size <= SZ_WORD; + + wb_valid <= 1'b0; wb_fault <= 1'b0; wb_reg_we <= 1'b0; wb_halt <= 1'b0; + wb_misaligned <= 1'b0; wb_cycle_dep <= 1'b0; wb_pc <= 32'b0; wb_insn <= 32'b0; + wb_val <= 32'b0; wb_next_pc <= 32'b0; wb_mem_addr <= 32'b0; wb_mem_rdata <= 32'b0; + wb_mem_wdata <= 32'b0; wb_mem_rmask <= 4'b0; wb_mem_wmask <= 4'b0; wb_sel <= WB_ALU; + end else begin + // ---- IF/ID: fetch every cycle unless stalled (hold pc and the IF/ID register). A taken + // redirect from EX replaces the pc and squashes the instruction being fetched; it + // always wins over a stall (hazard_unit never stalls during a redirect). + if (ex_redirect) begin + pc_q <= ex_next_pc; + id_valid <= 1'b0; + end else if (!stall) begin + pc_q <= pc_q + 32'd4; + id_valid <= 1'b1; + id_pc <= pc_q; + id_fetch_fault <= if_fault; + end + + // ---- ID -> EX: a redirect squashes the instruction in ID; a stall issues a bubble + ex_valid <= id_valid && !ex_redirect && !stall; + ex_illegal <= id_illegal || id_fetch_fault; + ex_pc <= id_pc; + ex_insn <= id_insn; + ex_rs1_data <= id_rs1_data; + ex_rs2_data <= id_rs2_data; + ex_imm <= id_imm; + ex_alu_op <= id_alu_op; + ex_a_pc <= id_a_pc; + ex_b_imm <= id_b_imm; + ex_reg_we <= id_reg_we; + ex_wb_sel <= id_wb_sel; + ex_mem_re <= id_mem_re; + ex_mem_we <= id_mem_we; + ex_mem_size <= id_mem_size; + ex_mem_unsigned <= id_mem_unsigned; + ex_is_branch <= id_is_branch; + ex_is_jal <= id_is_jal; + ex_is_jalr <= id_is_jalr; + ex_is_halt <= id_is_halt; + ex_uses_rs1 <= id_uses_rs1; + ex_uses_rs2 <= id_uses_rs2; + + // ---- EX -> MEM + mm_valid <= ex_valid; + mm_fault <= ex_fault; + mm_pc <= ex_pc; + mm_insn <= ex_insn; + mm_val <= ex_val; + mm_rs2_data <= ex_rs2_v; + mm_next_pc <= ex_next_pc; + mm_reg_we <= ex_reg_we; + mm_wb_sel <= ex_wb_sel; + mm_mem_re <= ex_mem_re; + mm_mem_we <= ex_mem_we; + mm_mem_size <= ex_mem_size; + mm_mem_unsigned <= ex_mem_unsigned; + mm_is_halt <= ex_is_halt; + + // ---- MEM -> WB + wb_valid <= mm_valid; + wb_fault <= mm_fault_total; + wb_halt <= mm_is_halt || (mem_effects_ok && mmio_tohost_we); + wb_pc <= mm_pc; + wb_insn <= mm_insn; + wb_val <= mm_mem_re ? lsu_load_value : mm_val; + wb_next_pc <= mm_next_pc; + wb_reg_we <= mm_reg_we; + wb_sel <= mm_wb_sel; + wb_mem_addr <= mm_mem_op ? lsu_word_addr : 32'b0; + wb_mem_rmask <= mm_mem_re ? lsu_lane_mask : 4'b0; + wb_mem_wmask <= mm_mem_we ? lsu_lane_mask : 4'b0; + wb_mem_rdata <= mm_mem_re ? lsu_rdata_lanes : 32'b0; + wb_mem_wdata <= mm_mem_we ? lsu_wdata : 32'b0; + wb_misaligned <= mm_mem_op && lsu_misaligned; + wb_cycle_dep <= (mm_mem_re && lsu_word_addr == CYCLE_ADDR) || + (mm_wb_sel == WB_CSR && (mm_insn[31:20] == 12'hC00 || mm_insn[31:20] == 12'hC01)); + end + end + + // ====================================================================== retire / status + always_ff @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + halted_q <= 1'b0; fault_q <= 1'b0; arch_pc_q <= IMEM_BASE; + rvfi_valid <= 1'b0; rvfi_order <= 32'b0; rvfi_pc <= 32'b0; rvfi_insn <= 32'b0; + rvfi_rd_addr <= 5'b0; rvfi_rd_wdata <= 32'b0; rvfi_pc_wdata <= 32'b0; + rvfi_mem_addr <= 32'b0; rvfi_mem_rmask <= 4'b0; rvfi_mem_wmask <= 4'b0; + rvfi_mem_rdata <= 32'b0; rvfi_mem_wdata <= 32'b0; rvfi_misaligned <= 1'b0; + rvfi_halt <= 1'b0; rvfi_cycle_dep <= 1'b0; + console_valid <= 1'b0; console_data <= 8'b0; tohost_valid <= 1'b0; tohost_data <= 32'b0; + end else begin + if (halt_now) halted_q <= 1'b1; + if (fault_now) fault_q <= 1'b1; + if (retire_now) arch_pc_q <= wb_next_pc; + + rvfi_valid <= retire_now; + rvfi_order <= instret_lo; + rvfi_pc <= wb_pc; + rvfi_insn <= wb_insn; + rvfi_rd_addr <= (wb_reg_we && wb_rd != 5'd0) ? wb_rd : 5'b0; + rvfi_rd_wdata <= (wb_reg_we && wb_rd != 5'd0) ? wb_wdata : 32'b0; + rvfi_pc_wdata <= wb_next_pc; + rvfi_mem_addr <= wb_mem_addr; + rvfi_mem_rmask <= wb_mem_rmask; + rvfi_mem_wmask <= wb_mem_wmask; + rvfi_mem_rdata <= wb_mem_rdata; + rvfi_mem_wdata <= wb_mem_wdata; + rvfi_misaligned <= wb_misaligned; + rvfi_halt <= halt_now; + rvfi_cycle_dep <= wb_cycle_dep; + + // MMIO effects happen in MEM (see mem_effects_ok); the byte shows up on the next cycle + console_valid <= mem_effects_ok && mmio_console_we; + console_data <= mmio_console_data; + if (mem_effects_ok && mmio_tohost_we) begin + tohost_valid <= 1'b1; + tohost_data <= mmio_tohost_data; + end + end + end + + assign halted = halted_q; + assign fault = fault_q; + assign dbg_pc = arch_pc_q; + assign dbg_cycle = cycle_lo; + assign dbg_instret = instret_lo; + + assign dbg_stage_valid = {wb_valid, mm_valid, ex_valid, id_valid, 1'b1}; + // IF has no instruction word yet (synchronous fetch): consumers look it up from the pc. + assign dbg_stage_pc = {wb_pc, mm_pc, ex_pc, id_pc, pc_q}; + assign dbg_stage_insn = {wb_insn, mm_insn, ex_insn, id_insn, 32'b0}; + assign dbg_flush_ifid = ex_redirect; + assign dbg_flush_idex = ex_redirect; + assign dbg_stall = stall; + assign dbg_load_use = load_use; + assign dbg_fwd_a = fwd_a; + assign dbg_fwd_b = fwd_b; + +endmodule diff --git a/rtl/core_single.sv b/rtl/core_single.sv new file mode 100644 index 0000000..473f1c5 --- /dev/null +++ b/rtl/core_single.sv @@ -0,0 +1,264 @@ +// Single-cycle RV32I core: fetch, decode, execute, memory and write-back in one clock. +// +// This is the reference microarchitecture: CPI is exactly 1, so cycles == instructions retired. +// It reuses the same alu / decoder / imm_gen / branch_unit / lsu / regfile as the pipelined +// core and is checked against the golden ISS (sim/golden/iss.py) in lockstep. +// +// Faults (illegal instruction, bus fault, misaligned/out-of-range jump) stop the core WITHOUT +// retiring the faulting instruction or changing any state - the ISS raises in exactly the same +// cases. Halting instructions (ecall, ebreak, a TOHOST store) retire, then stop the core. +// +// The rvfi_* outputs are REGISTERED: after a clock edge they describe the instruction that +// retired in the cycle that just ended (rvfi_valid is high for exactly that one cycle). +module core_single + import tiny_cpu_pkg::*; +( + input logic clk, + input logic rst_n, + + // Instruction memory, combinational read + output logic [IMEM_AW-1:0] imem_raddr, + input logic [31:0] imem_rdata, + + // Data memory, combinational read, byte-masked synchronous write + output logic [DMEM_AW-1:0] dmem_raddr, + input logic [31:0] dmem_rdata, + output logic [3:0] dmem_wmask, + output logic [DMEM_AW-1:0] dmem_waddr, + output logic [31:0] dmem_wdata, + + // Retire record (RVFI-lite, docs/PLAN.md section 2.4) + output logic rvfi_valid, + output logic [31:0] rvfi_order, + output logic [31:0] rvfi_pc, + output logic [31:0] rvfi_insn, + output logic [4:0] rvfi_rd_addr, + output logic [31:0] rvfi_rd_wdata, + output logic [31:0] rvfi_pc_wdata, + output logic [31:0] rvfi_mem_addr, + output logic [3:0] rvfi_mem_rmask, + output logic [3:0] rvfi_mem_wmask, + output logic [31:0] rvfi_mem_rdata, + output logic [31:0] rvfi_mem_wdata, + output logic rvfi_misaligned, + output logic rvfi_halt, + output logic rvfi_cycle_dep, + + // MMIO side effects (registered, valid for the cycle after the retiring store) + output logic console_valid, + output logic [7:0] console_data, + output logic tohost_valid, // sticky: a TOHOST store has happened + output logic [31:0] tohost_data, + + // Status and debug + output logic halted, + output logic fault, + output logic [31:0] dbg_pc, + output logic [32*32-1:0] dbg_regs, + output logic [31:0] dbg_cycle, + output logic [31:0] dbg_instret, + output logic [4:0] dbg_stage_valid, // single-cycle: only [0] (IF) is ever set + output logic [5*32-1:0] dbg_stage_pc, + output logic [5*32-1:0] dbg_stage_insn +); + + // ------------------------------------------------------------------ state + logic [31:0] pc_q; + logic halted_q, fault_q; + logic active; + + assign active = !halted_q && !fault_q; + + // ------------------------------------------------------------------ fetch + logic fetch_fault; + logic [31:0] insn; + + assign imem_raddr = pc_q[IMEM_AW+1:2]; + assign fetch_fault = |pc_q[31:IMEM_AW+2]; // pc outside IMEM (pc is always word-aligned) + assign insn = fetch_fault ? 32'b0 : imem_rdata; + + // ------------------------------------------------------------------ decode + alu_op_t alu_op; + imm_sel_t imm_sel; + wb_sel_t wb_sel; + mem_size_t mem_size; + logic a_pc, b_imm, reg_we, mem_re, mem_we, mem_unsigned; + logic is_branch, is_jal, is_jalr, is_halt, illegal; + /* verilator lint_off UNUSEDSIGNAL */ + logic uses_rs1, uses_rs2; // used by the pipelined core's hazard logic only + /* verilator lint_on UNUSEDSIGNAL */ + + decoder u_decoder ( + .insn(insn), .alu_op(alu_op), .a_pc(a_pc), .b_imm(b_imm), .imm_sel(imm_sel), + .reg_we(reg_we), .wb_sel(wb_sel), .mem_re(mem_re), .mem_we(mem_we), + .mem_size(mem_size), .mem_unsigned(mem_unsigned), .is_branch(is_branch), + .is_jal(is_jal), .is_jalr(is_jalr), .uses_rs1(uses_rs1), .uses_rs2(uses_rs2), + .is_halt(is_halt), .illegal(illegal) + ); + + logic [4:0] rs1, rs2, rd; + logic [31:0] imm; + + assign rs1 = insn[19:15]; + assign rs2 = insn[24:20]; + assign rd = insn[11:7]; + + imm_gen u_imm_gen (.insn(insn), .sel(imm_sel), .imm(imm)); + + // ------------------------------------------------------------------ execute + logic [31:0] rs1_data, rs2_data, alu_a, alu_b, alu_y; + logic br_taken; + logic wr_en; + logic [31:0] wb_data; + + assign alu_a = a_pc ? pc_q : rs1_data; + assign alu_b = b_imm ? imm : rs2_data; + + alu u_alu (.a(alu_a), .b(alu_b), .op(alu_op), .y(alu_y)); + branch_unit u_branch (.a(rs1_data), .b(rs2_data), .funct3(insn[14:12]), .taken(br_taken)); + + logic [31:0] pc_plus4, pc_target, next_pc; + logic redirect, target_misaligned; + + assign pc_plus4 = pc_q + 32'd4; + assign pc_target = pc_q + imm; + assign redirect = is_jal || is_jalr || (is_branch && br_taken); + always_comb begin + if (is_jalr) next_pc = {alu_y[31:1], 1'b0}; + else if (redirect) next_pc = pc_target; + else next_pc = pc_plus4; + end + assign target_misaligned = redirect && (|next_pc[1:0]); + + // ------------------------------------------------------------------ memory + logic [31:0] lsu_word_addr, lsu_wdata, lsu_rdata_lanes, lsu_load_value; + logic [3:0] lsu_lane_mask; + logic lsu_misaligned; + logic [31:0] mem_word; + logic hit_dmem, mmio_fault, mmio_console_we, mmio_tohost_we; + logic [31:0] mmio_rdata, mmio_tohost_data; + logic [7:0] mmio_console_data; + logic [31:0] cycle_lo, instret_lo; + + lsu u_lsu ( + .addr(alu_y), .size(mem_size), .is_unsigned(mem_unsigned), .store_data(rs2_data), + .mem_word(mem_word), .word_addr(lsu_word_addr), .lane_mask(lsu_lane_mask), + .wdata(lsu_wdata), .rdata_lanes(lsu_rdata_lanes), .load_value(lsu_load_value), + .misaligned(lsu_misaligned) + ); + + mmio u_mmio ( + .word_addr(lsu_word_addr), .re(mem_re), .we(mem_we), .wmask(lsu_lane_mask), + .wdata(lsu_wdata), .cycle(cycle_lo), .hit_dmem(hit_dmem), .mmio_rdata(mmio_rdata), + .console_we(mmio_console_we), .console_data(mmio_console_data), + .tohost_we(mmio_tohost_we), .tohost_data(mmio_tohost_data), .fault(mmio_fault) + ); + + assign dmem_raddr = lsu_word_addr[DMEM_AW+1:2]; + assign mem_word = hit_dmem ? dmem_rdata : mmio_rdata; + + // ------------------------------------------------------------------ CSRs + logic [31:0] csr_rdata; + logic retire_now, fault_now, halt_now; + + csr_counters u_csr ( + .clk(clk), .rst_n(rst_n), .retire(retire_now), .csr_addr(insn[31:20]), + .csr_rdata(csr_rdata), .cycle_lo(cycle_lo), .instret_lo(instret_lo) + ); + + // ------------------------------------------------------------------ retire / write-back + assign fault_now = active && (illegal || fetch_fault || mmio_fault || target_misaligned); + assign retire_now = active && !fault_now; + assign halt_now = retire_now && (is_halt || mmio_tohost_we); + + always_comb begin + case (wb_sel) + WB_MEM: wb_data = lsu_load_value; + WB_PC4: wb_data = pc_plus4; + WB_CSR: wb_data = csr_rdata; + default: wb_data = alu_y; + endcase + end + + assign wr_en = retire_now && reg_we && (rd != 5'd0); + + regfile #(.BYPASS(1'b0)) u_regfile ( + .clk(clk), .rst_n(rst_n), .raddr1(rs1), .raddr2(rs2), .rdata1(rs1_data), + .rdata2(rs2_data), .we(wr_en), .waddr(rd), .wdata(wb_data), .dbg_regs(dbg_regs) + ); + + assign dmem_wmask = (retire_now && mem_we && hit_dmem) ? lsu_lane_mask : 4'b0000; + assign dmem_waddr = lsu_word_addr[DMEM_AW+1:2]; + assign dmem_wdata = lsu_wdata; + + // ------------------------------------------------------------------ sequential state + logic mem_op, cycle_dep_now; + assign mem_op = mem_re || mem_we; + assign cycle_dep_now = (mem_re && lsu_word_addr == CYCLE_ADDR) || + (wb_sel == WB_CSR && (insn[31:20] == 12'hC00 || insn[31:20] == 12'hC01)); + + always_ff @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + pc_q <= IMEM_BASE; + halted_q <= 1'b0; + fault_q <= 1'b0; + rvfi_valid <= 1'b0; + rvfi_order <= 32'b0; + rvfi_pc <= 32'b0; + rvfi_insn <= 32'b0; + rvfi_rd_addr <= 5'b0; + rvfi_rd_wdata <= 32'b0; + rvfi_pc_wdata <= 32'b0; + rvfi_mem_addr <= 32'b0; + rvfi_mem_rmask <= 4'b0; + rvfi_mem_wmask <= 4'b0; + rvfi_mem_rdata <= 32'b0; + rvfi_mem_wdata <= 32'b0; + rvfi_misaligned <= 1'b0; + rvfi_halt <= 1'b0; + rvfi_cycle_dep <= 1'b0; + console_valid <= 1'b0; + console_data <= 8'b0; + tohost_valid <= 1'b0; + tohost_data <= 32'b0; + end else begin + if (retire_now) pc_q <= next_pc; + if (halt_now) halted_q <= 1'b1; + if (fault_now) fault_q <= 1'b1; + + rvfi_valid <= retire_now; + rvfi_order <= instret_lo; + rvfi_pc <= pc_q; + rvfi_insn <= insn; + rvfi_rd_addr <= (reg_we && rd != 5'd0) ? rd : 5'b0; + rvfi_rd_wdata <= (reg_we && rd != 5'd0) ? wb_data : 32'b0; + rvfi_pc_wdata <= next_pc; + rvfi_mem_addr <= mem_op ? lsu_word_addr : 32'b0; + rvfi_mem_rmask <= mem_re ? lsu_lane_mask : 4'b0; + rvfi_mem_wmask <= mem_we ? lsu_lane_mask : 4'b0; + rvfi_mem_rdata <= mem_re ? lsu_rdata_lanes : 32'b0; + rvfi_mem_wdata <= mem_we ? lsu_wdata : 32'b0; + rvfi_misaligned <= mem_op && lsu_misaligned; + rvfi_halt <= halt_now; + rvfi_cycle_dep <= cycle_dep_now; + + console_valid <= retire_now && mmio_console_we; + console_data <= mmio_console_data; + if (retire_now && mmio_tohost_we) begin + tohost_valid <= 1'b1; + tohost_data <= mmio_tohost_data; + end + end + end + + assign halted = halted_q; + assign fault = fault_q; + assign dbg_pc = pc_q; + assign dbg_cycle = cycle_lo; + assign dbg_instret = instret_lo; + + assign dbg_stage_valid = {4'b0, active}; + assign dbg_stage_pc = {128'b0, pc_q}; + assign dbg_stage_insn = {128'b0, insn}; + +endmodule diff --git a/rtl/csr_counters.sv b/rtl/csr_counters.sv new file mode 100644 index 0000000..2bfa151 --- /dev/null +++ b/rtl/csr_counters.sv @@ -0,0 +1,38 @@ +// Read-only Zicntr counters: cycle/time (the same counter) and instret, with the high halves. +module csr_counters ( + input logic clk, + input logic rst_n, + input logic retire, // an instruction retires this cycle + input logic [11:0] csr_addr, + output logic [31:0] csr_rdata, + output logic [31:0] cycle_lo, // also read through the CYCLE MMIO register + output logic [31:0] instret_lo +); + + logic [63:0] cycle_q; + logic [63:0] instret_q; + + always_ff @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + cycle_q <= 64'b0; + instret_q <= 64'b0; + end else begin + cycle_q <= cycle_q + 64'd1; + if (retire) instret_q <= instret_q + 64'd1; + end + end + + assign cycle_lo = cycle_q[31:0]; + assign instret_lo = instret_q[31:0]; + + always_comb begin + case (csr_addr) + 12'hC00, 12'hC01: csr_rdata = cycle_q[31:0]; + 12'hC02: csr_rdata = instret_q[31:0]; + 12'hC80, 12'hC81: csr_rdata = cycle_q[63:32]; + 12'hC82: csr_rdata = instret_q[63:32]; + default: csr_rdata = 32'b0; + endcase + end + +endmodule diff --git a/rtl/decoder.sv b/rtl/decoder.sv new file mode 100644 index 0000000..8e22573 --- /dev/null +++ b/rtl/decoder.sv @@ -0,0 +1,202 @@ +// Instruction decoder: RV32I (+ fence, ecall/ebreak, Zicntr csrrs-read) -> control signals. +// +// Legality is exactly the golden ISS's definition (sim/golden/decode.py plus the csrrs +// restrictions in iss.py): anything else sets `illegal`, and every side-effecting control is +// forced low so an illegal instruction can never write state. +module decoder + import tiny_cpu_pkg::*; +( + input logic [31:0] insn, + output alu_op_t alu_op, + output logic a_pc, // ALU operand A = pc (else rs1) + output logic b_imm, // ALU operand B = immediate (else rs2) + output imm_sel_t imm_sel, + output logic reg_we, // writes rd (the core still ignores rd == x0) + output wb_sel_t wb_sel, + output logic mem_re, + output logic mem_we, + output mem_size_t mem_size, + output logic mem_unsigned, + output logic is_branch, + output logic is_jal, + output logic is_jalr, + output logic uses_rs1, // reads rs1 (hazard detection, Phase 4) + output logic uses_rs2, + output logic is_halt, // ecall / ebreak + output logic illegal +); + + logic [6:0] opcode; + logic [2:0] funct3; + logic [6:0] funct7; + logic [11:0] csr; + logic csr_known; + + assign opcode = insn[6:0]; + assign funct3 = insn[14:12]; + assign funct7 = insn[31:25]; + assign csr = insn[31:20]; + + // cycle, time, instret and their high halves + assign csr_known = (csr == 12'hC00) || (csr == 12'hC01) || (csr == 12'hC02) || + (csr == 12'hC80) || (csr == 12'hC81) || (csr == 12'hC82); + + always_comb begin + alu_op = ALU_ADD; + a_pc = 1'b0; + b_imm = 1'b0; + imm_sel = IMM_I; + reg_we = 1'b0; + wb_sel = WB_ALU; + mem_re = 1'b0; + mem_we = 1'b0; + mem_size = SZ_WORD; + mem_unsigned = 1'b0; + is_branch = 1'b0; + is_jal = 1'b0; + is_jalr = 1'b0; + uses_rs1 = 1'b0; + uses_rs2 = 1'b0; + is_halt = 1'b0; + illegal = 1'b0; + + case (opcode) + OP_LUI: begin + alu_op = ALU_COPY_B; + b_imm = 1'b1; + imm_sel = IMM_U; + reg_we = 1'b1; + end + OP_AUIPC: begin + a_pc = 1'b1; + b_imm = 1'b1; + imm_sel = IMM_U; + reg_we = 1'b1; + end + OP_JAL: begin + imm_sel = IMM_J; + is_jal = 1'b1; + reg_we = 1'b1; + wb_sel = WB_PC4; + end + OP_JALR: begin + if (funct3 == 3'b000) begin + b_imm = 1'b1; + is_jalr = 1'b1; + reg_we = 1'b1; + wb_sel = WB_PC4; + uses_rs1 = 1'b1; + end else illegal = 1'b1; + end + OP_BRANCH: begin + if (funct3 == 3'b010 || funct3 == 3'b011) illegal = 1'b1; + else begin + imm_sel = IMM_B; + is_branch = 1'b1; + uses_rs1 = 1'b1; + uses_rs2 = 1'b1; + end + end + OP_LOAD: begin + b_imm = 1'b1; + mem_re = 1'b1; + reg_we = 1'b1; + wb_sel = WB_MEM; + uses_rs1 = 1'b1; + case (funct3) + 3'b000: mem_size = SZ_BYTE; + 3'b001: mem_size = SZ_HALF; + 3'b010: mem_size = SZ_WORD; + 3'b100: begin mem_size = SZ_BYTE; mem_unsigned = 1'b1; end + 3'b101: begin mem_size = SZ_HALF; mem_unsigned = 1'b1; end + default: illegal = 1'b1; + endcase + end + OP_STORE: begin + b_imm = 1'b1; + imm_sel = IMM_S; + mem_we = 1'b1; + uses_rs1 = 1'b1; + uses_rs2 = 1'b1; + case (funct3) + 3'b000: mem_size = SZ_BYTE; + 3'b001: mem_size = SZ_HALF; + 3'b010: mem_size = SZ_WORD; + default: illegal = 1'b1; + endcase + end + OP_IMM: begin + b_imm = 1'b1; + reg_we = 1'b1; + uses_rs1 = 1'b1; + case (funct3) + 3'b000: alu_op = ALU_ADD; + 3'b010: alu_op = ALU_SLT; + 3'b011: alu_op = ALU_SLTU; + 3'b100: alu_op = ALU_XOR; + 3'b110: alu_op = ALU_OR; + 3'b111: alu_op = ALU_AND; + 3'b001: begin + alu_op = ALU_SLL; + if (funct7 != 7'h00) illegal = 1'b1; + end + default: begin // 3'b101 + if (funct7 == 7'h00) alu_op = ALU_SRL; + else if (funct7 == 7'h20) alu_op = ALU_SRA; + else illegal = 1'b1; + end + endcase + end + OP_REG: begin + reg_we = 1'b1; + uses_rs1 = 1'b1; + uses_rs2 = 1'b1; + case (funct3) + 3'b000: begin + if (funct7 == 7'h00) alu_op = ALU_ADD; + else if (funct7 == 7'h20) alu_op = ALU_SUB; + else illegal = 1'b1; + end + 3'b101: begin + if (funct7 == 7'h00) alu_op = ALU_SRL; + else if (funct7 == 7'h20) alu_op = ALU_SRA; + else illegal = 1'b1; + end + default: begin + if (funct7 != 7'h00) illegal = 1'b1; + case (funct3) + 3'b001: alu_op = ALU_SLL; + 3'b010: alu_op = ALU_SLT; + 3'b011: alu_op = ALU_SLTU; + 3'b100: alu_op = ALU_XOR; + 3'b110: alu_op = ALU_OR; + default: alu_op = ALU_AND; // 3'b111 + endcase + end + endcase + end + OP_FENCE: begin + if (funct3 != 3'b000) illegal = 1'b1; // plain fence: a nop on this core + end + OP_SYSTEM: begin + if (insn == 32'h0000_0073 || insn == 32'h0010_0073) is_halt = 1'b1; + else if (funct3 == 3'b010 && insn[19:15] == 5'd0 && csr_known) begin + reg_we = 1'b1; + wb_sel = WB_CSR; + end else illegal = 1'b1; + end + default: illegal = 1'b1; + endcase + + if (illegal) begin // an illegal instruction must not touch any state + reg_we = 1'b0; + mem_re = 1'b0; + mem_we = 1'b0; + is_branch = 1'b0; + is_jal = 1'b0; + is_jalr = 1'b0; + is_halt = 1'b0; + end + end + +endmodule diff --git a/rtl/forward_unit.sv b/rtl/forward_unit.sv new file mode 100644 index 0000000..5e59ab4 --- /dev/null +++ b/rtl/forward_unit.sv @@ -0,0 +1,44 @@ +// Operand forwarding into EX. +// +// A consumer in EX takes its operand from the instruction one ahead (now in MEM; its EX/MEM result) +// or two ahead (now in WB; the value being written back, including load data and CSR reads) instead +// of the stale ID/EX copy. A producer three ahead is handled by the register file's write-first +// bypass. The nearer producer wins. Never forwards x0. With forward_en = 0 nothing is forwarded; +// the hazard unit then stalls so that the register file bypass is the only path. +// +// The MEM-stage source is only valid for results that exist at the end of EX (ALU, pc+4). Loads +// and CSR reads are "late": hazard_unit stalls their consumer one cycle so it arrives when they are +// in WB, and this unit then forwards from WB. +module forward_unit ( + input logic forward_en, + + input logic ex_valid, + input logic ex_uses_rs1, + input logic ex_uses_rs2, + input logic [4:0] ex_rs1, + input logic [4:0] ex_rs2, + + input logic mm_wr, // instruction in MEM writes a register + input logic [4:0] mm_rd, + input logic wb_wr, // instruction in WB writes a register (will retire) + input logic [4:0] wb_rd, + + output logic [1:0] fwd_a, // 0 = ID/EX value, 1 = EX/MEM, 2 = MEM/WB + output logic [1:0] fwd_b +); + + localparam logic [1:0] FWD_NONE = 2'd0; + localparam logic [1:0] FWD_EXMEM = 2'd1; + localparam logic [1:0] FWD_MEMWB = 2'd2; + + function automatic logic [1:0] pick(input logic uses, input logic [4:0] rs); + if (!forward_en || !ex_valid || !uses || rs == 5'd0) return FWD_NONE; + if (mm_wr && mm_rd == rs) return FWD_EXMEM; + if (wb_wr && wb_rd == rs) return FWD_MEMWB; + return FWD_NONE; + endfunction + + assign fwd_a = pick(ex_uses_rs1, ex_rs1); + assign fwd_b = pick(ex_uses_rs2, ex_rs2); + +endmodule diff --git a/rtl/hazard_unit.sv b/rtl/hazard_unit.sv new file mode 100644 index 0000000..e3cd526 --- /dev/null +++ b/rtl/hazard_unit.sv @@ -0,0 +1,43 @@ +// Data-hazard stall control for the instruction in ID. +// +// * forward_en = 1: the only stall is the load-use hazard - the instruction in EX is a load (or a +// CSR read) whose result the instruction in ID needs. One bubble; the consumer then reaches EX +// as the producer is in WB and takes it from there. +// * forward_en = 0: nothing is forwarded, so ID waits until every producer it depends on has +// left EX and MEM. A producer in WB is read through the register file's write-first bypass. +// This costs 2 stalls for a distance-1 dependency, 1 for distance 2, 0 for distance 3+. +// +// A redirect from EX squashes ID, so a stall for a wrong-path instruction is suppressed. +module hazard_unit ( + input logic forward_en, + input logic redirect, + + input logic id_valid, + input logic id_uses_rs1, + input logic id_uses_rs2, + input logic [4:0] id_rs1, + input logic [4:0] id_rs2, + + input logic ex_wr, // instruction in EX writes a register (rd != x0) + input logic ex_late, // ... and its result is only available in WB (load / CSR read) + input logic [4:0] ex_rd, + input logic mm_wr, + input logic [4:0] mm_rd, + + output logic stall, + output logic load_use +); + + logic ex_hit, mm_hit; + + function automatic logic needs(input logic uses, input logic [4:0] rs, input logic [4:0] rd); + return uses && (rs != 5'd0) && (rs == rd); + endfunction + + assign ex_hit = ex_wr && (needs(id_uses_rs1, id_rs1, ex_rd) || needs(id_uses_rs2, id_rs2, ex_rd)); + assign mm_hit = mm_wr && (needs(id_uses_rs1, id_rs1, mm_rd) || needs(id_uses_rs2, id_rs2, mm_rd)); + + assign load_use = id_valid && !redirect && ex_hit && ex_late; + assign stall = id_valid && !redirect && (forward_en ? (ex_hit && ex_late) : (ex_hit || mm_hit)); + +endmodule diff --git a/rtl/imm_gen.sv b/rtl/imm_gen.sv new file mode 100644 index 0000000..6da615b --- /dev/null +++ b/rtl/imm_gen.sv @@ -0,0 +1,23 @@ +// Immediate generator: one 32-bit sign-extended immediate per instruction format. +module imm_gen + import tiny_cpu_pkg::*; +( + /* verilator lint_off UNUSEDSIGNAL */ + input logic [31:0] insn, // opcode bits [6:0] are decoded elsewhere + /* verilator lint_on UNUSEDSIGNAL */ + input imm_sel_t sel, + output logic [31:0] imm +); + + always_comb begin + case (sel) + IMM_I: imm = {{20{insn[31]}}, insn[31:20]}; + IMM_S: imm = {{20{insn[31]}}, insn[31:25], insn[11:7]}; + IMM_B: imm = {{19{insn[31]}}, insn[31], insn[7], insn[30:25], insn[11:8], 1'b0}; + IMM_U: imm = {insn[31:12], 12'b0}; + IMM_J: imm = {{11{insn[31]}}, insn[31], insn[19:12], insn[20], insn[30:21], 1'b0}; + default: imm = 32'b0; + endcase + end + +endmodule diff --git a/rtl/lsu.sv b/rtl/lsu.sv new file mode 100644 index 0000000..be157d5 --- /dev/null +++ b/rtl/lsu.sv @@ -0,0 +1,67 @@ +// Load/store unit datapath: byte-lane positioning, masks, load extension. +// +// Misaligned accesses are not supported (docs/PLAN.md section 2.2): the address is forced into +// alignment for the access and `misaligned` flags it. `rdata_lanes`/`wdata` are lane-positioned +// words with unselected lanes zero - the same convention as the golden ISS retire record. +module lsu + import tiny_cpu_pkg::*; +( + input logic [31:0] addr, // effective byte address (rs1 + imm) + input mem_size_t size, + input logic is_unsigned, // loads: zero-extend instead of sign-extend + input logic [31:0] store_data, // rs2 + input logic [31:0] mem_word, // raw aligned word returned by memory / MMIO + output logic [31:0] word_addr, // addr with the low two bits cleared + output logic [3:0] lane_mask, // byte lanes touched, after forced alignment + output logic [31:0] wdata, // store_data moved into its lanes + output logic [31:0] rdata_lanes, // mem_word restricted to lane_mask + output logic [31:0] load_value, // extended, right-justified load result + output logic misaligned +); + + logic [1:0] off; // byte offset after forced alignment + logic [31:0] size_mask; // low 8/16/32 bits + logic [31:0] lane_bits; // lane_mask expanded to a bit mask + logic [31:0] shifted; + + assign word_addr = {addr[31:2], 2'b00}; + + always_comb begin + case (size) + SZ_BYTE: begin + off = addr[1:0]; + lane_mask = 4'b0001 << off; + size_mask = 32'h0000_00FF; + misaligned = 1'b0; + end + SZ_HALF: begin + off = {addr[1], 1'b0}; + lane_mask = 4'b0011 << off; + size_mask = 32'h0000_FFFF; + misaligned = addr[0]; + end + default: begin + off = 2'b00; + lane_mask = 4'b1111; + size_mask = 32'hFFFF_FFFF; + misaligned = |addr[1:0]; + end + endcase + end + + assign lane_bits = {{8{lane_mask[3]}}, {8{lane_mask[2]}}, {8{lane_mask[1]}}, {8{lane_mask[0]}}}; + assign wdata = (store_data & size_mask) << {off, 3'b000}; + assign rdata_lanes = mem_word & lane_bits; + assign shifted = rdata_lanes >> {off, 3'b000}; + + always_comb begin + case (size) + SZ_BYTE: load_value = is_unsigned ? {24'b0, shifted[7:0]} + : {{24{shifted[7]}}, shifted[7:0]}; + SZ_HALF: load_value = is_unsigned ? {16'b0, shifted[15:0]} + : {{16{shifted[15]}}, shifted[15:0]}; + default: load_value = shifted; + endcase + end + +endmodule diff --git a/rtl/mem_comb.sv b/rtl/mem_comb.sv new file mode 100644 index 0000000..80ab3c6 --- /dev/null +++ b/rtl/mem_comb.sv @@ -0,0 +1,39 @@ +// Simulation memory with a COMBINATIONAL read port (used by the single-cycle core). +// Not FPGA-friendly - the pipelined core uses mem_sync.sv (Phase 3) for BRAM inference. +// +// Two write paths: the core's byte-masked write, and a full-word `ld_*` loader port the +// testbench/harness uses to place a program image while the core is in reset. +module mem_comb #( + parameter int unsigned WORDS = 1024, + localparam int unsigned AW = $clog2(WORDS) +) ( + input logic clk, + input logic [AW-1:0] raddr, // word index + output logic [31:0] rdata, + input logic [3:0] wmask, // per-byte write enables + input logic [AW-1:0] waddr, + input logic [31:0] wdata, + input logic ld_en, + input logic [AW-1:0] ld_addr, + input logic [31:0] ld_data +); + + logic [31:0] mem [WORDS]; + + initial begin + for (int i = 0; i < WORDS; i++) mem[i] = 32'b0; + end + + always_ff @(posedge clk) begin + if (ld_en) begin + mem[ld_addr] <= ld_data; + end else begin + for (int b = 0; b < 4; b++) begin + if (wmask[b]) mem[waddr][8*b +: 8] <= wdata[8*b +: 8]; + end + end + end + + assign rdata = mem[raddr]; + +endmodule diff --git a/rtl/mem_sync.sv b/rtl/mem_sync.sv new file mode 100644 index 0000000..39e93ae --- /dev/null +++ b/rtl/mem_sync.sv @@ -0,0 +1,53 @@ +// Memory with a SYNCHRONOUS read port (the read result appears one clock after the address) - +// the shape FPGA block RAM has, used by the pipelined core. +// +// * `re` low holds the read register (used to stall fetch in Phase 4). +// * READ_NEW=1: if the same clock edge also writes the address being read, the read returns the +// NEW bytes (write-first). The pipeline needs this: a load reads DMEM in EX while the +// store ahead of it writes in MEM, i.e. at the same edge, and must see the stored data. +// READ_NEW=0 gives plain read-before-write behaviour. +// * Two write paths, as in mem_comb: the core's byte-masked write and a word-wide loader port. +module mem_sync #( + parameter int unsigned WORDS = 1024, + parameter bit READ_NEW = 1'b1, + localparam int unsigned AW = $clog2(WORDS) +) ( + input logic clk, + input logic re, + input logic [AW-1:0] raddr, + output logic [31:0] rdata, + input logic [3:0] wmask, + input logic [AW-1:0] waddr, + input logic [31:0] wdata, + input logic ld_en, + input logic [AW-1:0] ld_addr, + input logic [31:0] ld_data +); + + logic [31:0] mem [WORDS]; + + initial begin + for (int i = 0; i < WORDS; i++) mem[i] = 32'b0; + rdata = 32'b0; + end + + always_ff @(posedge clk) begin + if (ld_en) begin + mem[ld_addr] <= ld_data; + end else begin + for (int b = 0; b < 4; b++) begin + if (wmask[b]) mem[waddr][8*b +: 8] <= wdata[8*b +: 8]; + end + end + + if (re) begin + rdata <= mem[raddr]; // old contents; the merge below overrides written bytes + if (READ_NEW && !ld_en && waddr == raddr) begin + for (int b = 0; b < 4; b++) begin + if (wmask[b]) rdata[8*b +: 8] <= wdata[8*b +: 8]; + end + end + end + end + +endmodule diff --git a/rtl/mmio.sv b/rtl/mmio.sv new file mode 100644 index 0000000..b1a47c8 --- /dev/null +++ b/rtl/mmio.sv @@ -0,0 +1,44 @@ +// Data-side address decode and the three MMIO registers (docs/PLAN.md section 2.1). +// CONSOLE 0x0002_0000 write-only; lane-0 byte is printed +// TOHOST 0x0002_0004 write-only, word stores only; halts the core +// CYCLE 0x0002_0008 read-only cycle counter +// `fault` follows the golden ISS exactly: loads may hit DMEM or CYCLE; stores may hit DMEM, +// CONSOLE, or TOHOST (word-wide). Everything else - IMEM, unmapped, read of a write-only +// register, write of a read-only one - is a bus fault. +module mmio + import tiny_cpu_pkg::*; +( + input logic [31:0] word_addr, // word-aligned address from the LSU + input logic re, + input logic we, + input logic [3:0] wmask, + input logic [31:0] wdata, // lane-positioned + input logic [31:0] cycle, + output logic hit_dmem, + output logic [31:0] mmio_rdata, + output logic console_we, + output logic [7:0] console_data, + output logic tohost_we, + output logic [31:0] tohost_data, + output logic fault +); + + localparam logic [31:0] DMEM_BYTES = 32'(DMEM_WORDS * 4); + + logic hit_console, hit_tohost, hit_cycle; + + assign hit_dmem = (word_addr >= DMEM_BASE) && (word_addr < DMEM_BASE + DMEM_BYTES); + assign hit_console = (word_addr == CONSOLE_ADDR); + assign hit_tohost = (word_addr == TOHOST_ADDR); + assign hit_cycle = (word_addr == CYCLE_ADDR); + + assign mmio_rdata = hit_cycle ? cycle : 32'b0; + assign console_we = we && hit_console && wmask[0]; + assign console_data = wdata[7:0]; + assign tohost_we = we && hit_tohost && (wmask == 4'hF); + assign tohost_data = wdata; + + assign fault = (re && !(hit_dmem || hit_cycle)) || + (we && !(hit_dmem || hit_console || (hit_tohost && wmask == 4'hF))); + +endmodule diff --git a/rtl/regfile.sv b/rtl/regfile.sv new file mode 100644 index 0000000..09a08c2 --- /dev/null +++ b/rtl/regfile.sv @@ -0,0 +1,44 @@ +// 32 x 32-bit register file: two combinational read ports, one write port. +// * x0 reads as zero and ignores writes. +// * BYPASS=1 (pipelined core): write-first bypass - a read of the register being written this +// cycle returns the write data, removing the WB->ID hazard without an extra forwarding path. +// BYPASS=0 (single-cycle core): the writer IS the reader, so bypassing would feed an +// instruction's own result back into its operands (`lw x1,0(x1)`) and create a +// combinational loop. Reads always return the old register value. +// * Registers reset to zero so simulations are deterministic. +module regfile #( + parameter bit BYPASS = 1'b1 +) ( + input logic clk, + input logic rst_n, + input logic [4:0] raddr1, + input logic [4:0] raddr2, + output logic [31:0] rdata1, + output logic [31:0] rdata2, + input logic we, + input logic [4:0] waddr, + input logic [31:0] wdata, + output logic [32*32-1:0] dbg_regs // register i at [32*i +: 32] +); + + logic [31:0] regs [32]; + logic wr; + + assign wr = we && (waddr != 5'd0); + + always_ff @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + for (int i = 0; i < 32; i++) regs[i] <= 32'b0; + end else if (wr) begin + regs[waddr] <= wdata; + end + end + + assign rdata1 = (BYPASS && wr && waddr == raddr1) ? wdata : regs[raddr1]; + assign rdata2 = (BYPASS && wr && waddr == raddr2) ? wdata : regs[raddr2]; + + for (genvar i = 0; i < 32; i++) begin : g_dbg + assign dbg_regs[32*i +: 32] = regs[i]; + end + +endmodule diff --git a/rtl/tiny_cpu_pkg.sv b/rtl/tiny_cpu_pkg.sv new file mode 100644 index 0000000..c0239e6 --- /dev/null +++ b/rtl/tiny_cpu_pkg.sv @@ -0,0 +1,68 @@ +// Shared constants and enums. The numeric encodings here are mirrored in sim/consts.py and +// checked against this file by sim/golden/test_consts.py - change both together. +/* verilator lint_off UNUSEDPARAM */ +package tiny_cpu_pkg; + + // ---- Memory map (docs/PLAN.md section 2.1) ------------------------------------------- + localparam int unsigned IMEM_WORDS = 1024; // 4 KiB + localparam int unsigned DMEM_WORDS = 1024; // 4 KiB + localparam int unsigned IMEM_AW = $clog2(IMEM_WORDS); + localparam int unsigned DMEM_AW = $clog2(DMEM_WORDS); + + localparam logic [31:0] IMEM_BASE = 32'h0000_0000; + localparam logic [31:0] DMEM_BASE = 32'h0001_0000; + localparam logic [31:0] CONSOLE_ADDR = 32'h0002_0000; + localparam logic [31:0] TOHOST_ADDR = 32'h0002_0004; + localparam logic [31:0] CYCLE_ADDR = 32'h0002_0008; + + // ---- RV32I major opcodes ------------------------------------------------------------- + localparam logic [6:0] OP_LUI = 7'h37; + localparam logic [6:0] OP_AUIPC = 7'h17; + localparam logic [6:0] OP_JAL = 7'h6F; + localparam logic [6:0] OP_JALR = 7'h67; + localparam logic [6:0] OP_BRANCH = 7'h63; + localparam logic [6:0] OP_LOAD = 7'h03; + localparam logic [6:0] OP_STORE = 7'h23; + localparam logic [6:0] OP_IMM = 7'h13; + localparam logic [6:0] OP_REG = 7'h33; + localparam logic [6:0] OP_FENCE = 7'h0F; + localparam logic [6:0] OP_SYSTEM = 7'h73; + + // ---- Control encodings --------------------------------------------------------------- + typedef enum logic [3:0] { + ALU_ADD = 4'd0, + ALU_SUB = 4'd1, + ALU_SLL = 4'd2, + ALU_SLT = 4'd3, + ALU_SLTU = 4'd4, + ALU_XOR = 4'd5, + ALU_SRL = 4'd6, + ALU_SRA = 4'd7, + ALU_OR = 4'd8, + ALU_AND = 4'd9, + ALU_COPY_B = 4'd10 // result = operand B (LUI) + } alu_op_t; + + typedef enum logic [2:0] { + IMM_I = 3'd0, + IMM_S = 3'd1, + IMM_B = 3'd2, + IMM_U = 3'd3, + IMM_J = 3'd4 + } imm_sel_t; + + typedef enum logic [1:0] { + WB_ALU = 2'd0, + WB_MEM = 2'd1, + WB_PC4 = 2'd2, + WB_CSR = 2'd3 + } wb_sel_t; + + typedef enum logic [1:0] { + SZ_BYTE = 2'd0, + SZ_HALF = 2'd1, + SZ_WORD = 2'd2 + } mem_size_t; + +endpackage +/* verilator lint_on UNUSEDPARAM */ diff --git a/rtl/tiny_cpu_top.sv b/rtl/tiny_cpu_top.sv new file mode 100644 index 0000000..019e9d6 --- /dev/null +++ b/rtl/tiny_cpu_top.sv @@ -0,0 +1,144 @@ +// Top level: a core plus its memories, with the program loader and the debug/retire bus. +// +// `CORE` selects the microarchitecture: "SINGLE" (reference, CPI 1) or "PIPE" (5-stage). The port list +// is identical for both so the testbenches and the WASM harness never change. +// +// Loading a program: hold rst_n low, then for each word pulse load_en with load_sel (0 = IMEM, +// 1 = DMEM), load_addr (word index) and load_data. Release rst_n to run. +module tiny_cpu_top + import tiny_cpu_pkg::*; +#( + parameter string CORE = "SINGLE", + // loader address width: wide enough for the larger memory + localparam int unsigned LAW = (IMEM_AW > DMEM_AW) ? IMEM_AW : DMEM_AW +) ( + input logic clk, + input logic rst_n, + input logic cfg_forward_en, // PIPE: 1 = forwarding on; 0 = stall on every RAW hazard + + input logic load_en, + input logic load_sel, + input logic [LAW-1:0] load_addr, + input logic [31:0] load_data, + + output logic rvfi_valid, + output logic [31:0] rvfi_order, + output logic [31:0] rvfi_pc, + output logic [31:0] rvfi_insn, + output logic [4:0] rvfi_rd_addr, + output logic [31:0] rvfi_rd_wdata, + output logic [31:0] rvfi_pc_wdata, + output logic [31:0] rvfi_mem_addr, + output logic [3:0] rvfi_mem_rmask, + output logic [3:0] rvfi_mem_wmask, + output logic [31:0] rvfi_mem_rdata, + output logic [31:0] rvfi_mem_wdata, + output logic rvfi_misaligned, + output logic rvfi_halt, + output logic rvfi_cycle_dep, + + output logic console_valid, + output logic [7:0] console_data, + output logic tohost_valid, + output logic [31:0] tohost_data, + + output logic halted, + output logic fault, + output logic [31:0] dbg_pc, + output logic [32*32-1:0] dbg_regs, + output logic [31:0] dbg_cycle, + output logic [31:0] dbg_instret, + + // Per-stage view for the visualizer: stage index 0=IF 1=ID 2=EX 3=MEM 4=WB. "SINGLE" only + // ever populates IF and reports no hazards. dbg_fwd_a/b: 0 none, 1 EX/MEM, 2 MEM/WB. + output logic [4:0] dbg_stage_valid, + output logic [5*32-1:0] dbg_stage_pc, + output logic [5*32-1:0] dbg_stage_insn, + output logic dbg_flush_ifid, + output logic dbg_flush_idex, + output logic dbg_stall, + output logic dbg_load_use, + output logic [1:0] dbg_fwd_a, + output logic [1:0] dbg_fwd_b +); + + if (CORE == "SINGLE") begin : g_single + logic [IMEM_AW-1:0] imem_raddr; + logic [31:0] imem_rdata; + logic [DMEM_AW-1:0] dmem_raddr, dmem_waddr; + logic [31:0] dmem_rdata, dmem_wdata; + logic [3:0] dmem_wmask; + + core_single u_core ( + .clk, .rst_n, + .imem_raddr, .imem_rdata, + .dmem_raddr, .dmem_rdata, .dmem_wmask, .dmem_waddr, .dmem_wdata, + .rvfi_valid, .rvfi_order, .rvfi_pc, .rvfi_insn, .rvfi_rd_addr, .rvfi_rd_wdata, + .rvfi_pc_wdata, .rvfi_mem_addr, .rvfi_mem_rmask, .rvfi_mem_wmask, .rvfi_mem_rdata, + .rvfi_mem_wdata, .rvfi_misaligned, .rvfi_halt, .rvfi_cycle_dep, + .console_valid, .console_data, .tohost_valid, .tohost_data, + .halted, .fault, .dbg_pc, .dbg_regs, .dbg_cycle, .dbg_instret, + .dbg_stage_valid, .dbg_stage_pc, .dbg_stage_insn + ); + + // The single-cycle core has no hazards and ignores cfg_forward_en. + /* verilator lint_off UNUSEDSIGNAL */ + logic unused_cfg; + assign unused_cfg = cfg_forward_en; + /* verilator lint_on UNUSEDSIGNAL */ + assign dbg_flush_ifid = 1'b0; + assign dbg_flush_idex = 1'b0; + assign dbg_stall = 1'b0; + assign dbg_load_use = 1'b0; + assign dbg_fwd_a = 2'b00; + assign dbg_fwd_b = 2'b00; + + mem_comb #(.WORDS(IMEM_WORDS)) u_imem ( + .clk, .raddr(imem_raddr), .rdata(imem_rdata), + .wmask(4'b0000), .waddr('0), .wdata(32'b0), // IMEM is read-only to the core + .ld_en(load_en && !load_sel), .ld_addr(load_addr[IMEM_AW-1:0]), .ld_data(load_data) + ); + + mem_comb #(.WORDS(DMEM_WORDS)) u_dmem ( + .clk, .raddr(dmem_raddr), .rdata(dmem_rdata), + .wmask(dmem_wmask), .waddr(dmem_waddr), .wdata(dmem_wdata), + .ld_en(load_en && load_sel), .ld_addr(load_addr[DMEM_AW-1:0]), .ld_data(load_data) + ); + end else if (CORE == "PIPE") begin : g_pipe + logic imem_re; + logic [IMEM_AW-1:0] imem_raddr; + logic [31:0] imem_rdata; + logic [DMEM_AW-1:0] dmem_raddr, dmem_waddr; + logic [31:0] dmem_rdata, dmem_wdata; + logic [3:0] dmem_wmask; + + core_pipe u_core ( + .clk, .rst_n, .cfg_forward_en, + .imem_re, .imem_raddr, .imem_rdata, + .dmem_raddr, .dmem_rdata, .dmem_wmask, .dmem_waddr, .dmem_wdata, + .rvfi_valid, .rvfi_order, .rvfi_pc, .rvfi_insn, .rvfi_rd_addr, .rvfi_rd_wdata, + .rvfi_pc_wdata, .rvfi_mem_addr, .rvfi_mem_rmask, .rvfi_mem_wmask, .rvfi_mem_rdata, + .rvfi_mem_wdata, .rvfi_misaligned, .rvfi_halt, .rvfi_cycle_dep, + .console_valid, .console_data, .tohost_valid, .tohost_data, + .halted, .fault, .dbg_pc, .dbg_regs, .dbg_cycle, .dbg_instret, + .dbg_stage_valid, .dbg_stage_pc, .dbg_stage_insn, .dbg_flush_ifid, .dbg_flush_idex, + .dbg_stall, .dbg_load_use, .dbg_fwd_a, .dbg_fwd_b + ); + + // IMEM is read-only to the core. DMEM read-new: a load in EX sees the store ahead of it in MEM. + mem_sync #(.WORDS(IMEM_WORDS), .READ_NEW(1'b0)) u_imem ( + .clk, .re(imem_re), .raddr(imem_raddr), .rdata(imem_rdata), + .wmask(4'b0000), .waddr('0), .wdata(32'b0), + .ld_en(load_en && !load_sel), .ld_addr(load_addr[IMEM_AW-1:0]), .ld_data(load_data) + ); + + mem_sync #(.WORDS(DMEM_WORDS), .READ_NEW(1'b1)) u_dmem ( + .clk, .re(1'b1), .raddr(dmem_raddr), .rdata(dmem_rdata), + .wmask(dmem_wmask), .waddr(dmem_waddr), .wdata(dmem_wdata), + .ld_en(load_en && load_sel), .ld_addr(load_addr[DMEM_AW-1:0]), .ld_data(load_data) + ); + end else begin : g_unsupported + $error("tiny_cpu_top: unknown CORE=\"%s\" (use \"SINGLE\" or \"PIPE\")", CORE); + end + +endmodule diff --git a/scripts/env.sh b/scripts/env.sh index 07c2e75..dcb8ff0 100644 --- a/scripts/env.sh +++ b/scripts/env.sh @@ -6,5 +6,6 @@ _ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" unset VERILATOR_ROOT # Verilator derives it from its own location; setting it breaks it export PATH="$_ROOT/.tools/verilator-install/bin:$_ROOT/.venv/bin:$PATH" +[ -d "$_ROOT/.tools/riscv-gcc/bin" ] && export PATH="$_ROOT/.tools/riscv-gcc/bin:$PATH" [ -f "$_ROOT/.tools/emsdk/emsdk_env.sh" ] && source "$_ROOT/.tools/emsdk/emsdk_env.sh" >/dev/null 2>&1 unset _ROOT diff --git a/scripts/lint.sh b/scripts/lint.sh new file mode 100644 index 0000000..80703e3 --- /dev/null +++ b/scripts/lint.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Verilator -Wall lint of every RTL module standalone, plus the whole design from tiny_cpu_top. +# Usage: bash scripts/lint.sh (needs `source scripts/env.sh`). Prints only failures. +set -uo pipefail +cd "$(dirname "$0")/.." + +PKG=rtl/tiny_cpu_pkg.sv +LEAVES="alu imm_gen branch_unit lsu decoder regfile csr_counters mmio mem_comb mem_sync forward_unit hazard_unit" +ALL="$PKG"; for m in $LEAVES core_single core_pipe tiny_cpu_top; do ALL+=" rtl/$m.sv"; done +status=0 + +lint() { # lint [-G...] + local top=$1; shift + if ! out=$(verilator --lint-only -Wall --top-module "$top" "$@" 2>&1); then + echo "LINT FAILED: $top"; echo "$out"; status=1 + elif echo "$out" | grep -q '^%Warning'; then + echo "LINT WARNINGS: $top"; echo "$out"; status=1 + else + echo "ok $top" + fi +} + +for m in $LEAVES; do lint "$m" "$PKG" "rtl/$m.sv"; done +lint core_single $ALL +lint core_pipe $ALL +lint tiny_cpu_top $ALL +lint tiny_cpu_top '-GCORE="PIPE"' $ALL # the PIPE generate branch is not elaborated by default +lint counter rtl/spike/counter.sv +exit $status diff --git a/sim/Makefile b/sim/Makefile index 98bd702..2a8c1a0 100644 --- a/sim/Makefile +++ b/sim/Makefile @@ -1,11 +1,11 @@ # cocotb + Verilator. Defaults run the Phase 0 counter test. -# make MODULE=test_counter TOPLEVEL=counter VERILOG_SOURCES="$PWD/../rtl/spike/counter.sv" +# make COCOTB_TEST_MODULES=test_counter TOPLEVEL=counter VERILOG_SOURCES="$PWD/../rtl/spike/counter.sv" SIM ?= verilator TOPLEVEL_LANG ?= verilog VERILOG_SOURCES ?= $(abspath ../rtl/spike/counter.sv) TOPLEVEL ?= counter -MODULE ?= test_counter +COCOTB_TEST_MODULES ?= test_counter # Waveforms land in sim_build/dump.fst (open with gtkwave). CI passes WAVES=0. WAVES ?= 1 diff --git a/sim/consts.py b/sim/consts.py new file mode 100644 index 0000000..36d8dbb --- /dev/null +++ b/sim/consts.py @@ -0,0 +1,13 @@ +"""Numeric control encodings mirrored from rtl/tiny_cpu_pkg.sv (checked by golden/test_consts.py).""" + +ALU = dict(ADD=0, SUB=1, SLL=2, SLT=3, SLTU=4, XOR=5, SRL=6, SRA=7, OR=8, AND=9, COPY_B=10) +IMM = dict(I=0, S=1, B=2, U=3, J=4) +WB = dict(ALU=0, MEM=1, PC4=2, CSR=3) +SZ = dict(BYTE=0, HALF=1, WORD=2) + +# Memory map (docs/PLAN.md section 2.1) +IMEM_WORDS = DMEM_WORDS = 1024 +DMEM_BASE = 0x0001_0000 +CONSOLE_ADDR = 0x0002_0000 +TOHOST_ADDR = 0x0002_0004 +CYCLE_ADDR = 0x0002_0008 diff --git a/sim/golden/.gitkeep b/sim/golden/__init__.py similarity index 100% rename from sim/golden/.gitkeep rename to sim/golden/__init__.py diff --git a/sim/golden/cli.py b/sim/golden/cli.py new file mode 100644 index 0000000..599cd61 --- /dev/null +++ b/sim/golden/cli.py @@ -0,0 +1,48 @@ +"""Run a built image on the golden ISS. + + python -m golden.cli run ../sw/build/programs/fib # prints console + summary + python -m golden.cli run ../sw/build/programs/fib --trace # + every retired instruction + python -m golden.cli run --trace-last 20 # only the last 20 retires + + is the path without `.imem.bin` / `.dmem.bin` (see sw/Makefile). Exit status is 0 on PASS. +""" + +from __future__ import annotations + +import argparse +import sys + +from golden.iss import Iss, IssError + + +def main(argv: list[str] | None = None) -> int: + ap = argparse.ArgumentParser(prog="golden.cli", description=__doc__.split("\n")[0]) + sub = ap.add_subparsers(dest="cmd", required=True) + run = sub.add_parser("run", help="run an image to halt") + run.add_argument("stem") + run.add_argument("--max", type=int, default=5_000_000, help="instruction limit") + run.add_argument("--trace", action="store_true", help="print every retire record") + run.add_argument("--trace-last", type=int, metavar="N", help="print the last N retire records") + args = ap.parse_args(argv) + + iss = Iss.from_build(args.stem) + trace = [] + try: + trace = iss.run(args.max) + except (IssError, TimeoutError) as exc: + print(f"ERROR: {exc}", file=sys.stderr) + return 2 + finally: + if args.trace: + print(*trace, sep="\n") + elif args.trace_last: + print(*trace[-args.trace_last :], sep="\n") + + print(iss.console_text, end="" if iss.console_text.endswith("\n") else "\n") + status = "PASS" if iss.passed else f"FAIL(tohost={iss.tohost})" + print(f"[{status}] {iss.instret} instructions, halted by {iss.halt_reason}", file=sys.stderr) + return 0 if iss.passed else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/sim/golden/decode.py b/sim/golden/decode.py new file mode 100644 index 0000000..dcb8f4d --- /dev/null +++ b/sim/golden/decode.py @@ -0,0 +1,283 @@ +"""RV32I decode tables, encoder and disassembler. Single source of truth for the ISA. + +`web/src/lib/isa.ts` is generated from INSNS by gen_isa_ts.py, and the RTL decoder tests +(Phase 2) derive their expected control bundles from here, so Python, TypeScript and +SystemVerilog all agree on one opcode table. +""" + +from __future__ import annotations + +from dataclasses import dataclass + +M32 = 0xFFFFFFFF + +REG_NAMES = ( + "zero ra sp gp tp t0 t1 t2 s0 s1 a0 a1 a2 a3 a4 a5 a6 a7 " + "s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 t3 t4 t5 t6" +).split() + +# Formats. SH = shift-immediate (shamt in the rs2 field), CSR = Zicntr read, SYS = ecall/ebreak. +FORMATS = ("R", "I", "S", "B", "U", "J", "SH", "FENCE", "CSR", "SYS") + +# CSRs the core implements (read-only counters, Zicntr subset). +CSR_NAMES = { + 0xC00: "cycle", + 0xC01: "time", + 0xC02: "instret", + 0xC80: "cycleh", + 0xC81: "timeh", + 0xC82: "instreth", +} + + +@dataclass(frozen=True) +class InsnDef: + name: str + fmt: str + mask: int + match: int + + +def _def(name: str, fmt: str, opcode: int, f3: int | None = None, f7: int | None = None) -> InsnDef: + mask, match = 0x7F, opcode + if f3 is not None: + mask |= 0x7 << 12 + match |= f3 << 12 + if f7 is not None: + mask |= 0x7F << 25 + match |= f7 << 25 + return InsnDef(name, fmt, mask, match) + + +INSNS: tuple[InsnDef, ...] = ( + _def("lui", "U", 0x37), + _def("auipc", "U", 0x17), + _def("jal", "J", 0x6F), + _def("jalr", "I", 0x67, 0), + _def("beq", "B", 0x63, 0), + _def("bne", "B", 0x63, 1), + _def("blt", "B", 0x63, 4), + _def("bge", "B", 0x63, 5), + _def("bltu", "B", 0x63, 6), + _def("bgeu", "B", 0x63, 7), + _def("lb", "I", 0x03, 0), + _def("lh", "I", 0x03, 1), + _def("lw", "I", 0x03, 2), + _def("lbu", "I", 0x03, 4), + _def("lhu", "I", 0x03, 5), + _def("sb", "S", 0x23, 0), + _def("sh", "S", 0x23, 1), + _def("sw", "S", 0x23, 2), + _def("addi", "I", 0x13, 0), + _def("slti", "I", 0x13, 2), + _def("sltiu", "I", 0x13, 3), + _def("xori", "I", 0x13, 4), + _def("ori", "I", 0x13, 6), + _def("andi", "I", 0x13, 7), + _def("slli", "SH", 0x13, 1, 0x00), + _def("srli", "SH", 0x13, 5, 0x00), + _def("srai", "SH", 0x13, 5, 0x20), + _def("add", "R", 0x33, 0, 0x00), + _def("sub", "R", 0x33, 0, 0x20), + _def("sll", "R", 0x33, 1, 0x00), + _def("slt", "R", 0x33, 2, 0x00), + _def("sltu", "R", 0x33, 3, 0x00), + _def("xor", "R", 0x33, 4, 0x00), + _def("srl", "R", 0x33, 5, 0x00), + _def("sra", "R", 0x33, 5, 0x20), + _def("or", "R", 0x33, 6, 0x00), + _def("and", "R", 0x33, 7, 0x00), + _def("fence", "FENCE", 0x0F, 0), + InsnDef("ecall", "SYS", 0xFFFFFFFF, 0x00000073), + InsnDef("ebreak", "SYS", 0xFFFFFFFF, 0x00100073), + _def("csrrs", "CSR", 0x73, 2), +) + +BY_NAME = {d.name: d for d in INSNS} +LOADS = {"lb", "lh", "lw", "lbu", "lhu"} +STORES = {"sb", "sh", "sw"} +BRANCHES = {"beq", "bne", "blt", "bge", "bltu", "bgeu"} + + +def sext(value: int, bits: int) -> int: + value &= (1 << bits) - 1 + return value - (1 << bits) if value & (1 << (bits - 1)) else value + + +def s32(value: int) -> int: + return sext(value, 32) + + +@dataclass(frozen=True) +class Decoded: + name: str + fmt: str + rd: int + rs1: int + rs2: int + imm: int # signed Python int; shamt for SH; unused (0) for R/FENCE/SYS + csr: int + insn: int + + +def imm_i(insn: int) -> int: + return sext(insn >> 20, 12) + + +def imm_s(insn: int) -> int: + return sext(((insn >> 25) << 5) | ((insn >> 7) & 0x1F), 12) + + +def imm_b(insn: int) -> int: + raw = ( + ((insn >> 31) << 12) + | (((insn >> 7) & 1) << 11) + | (((insn >> 25) & 0x3F) << 5) + | (((insn >> 8) & 0xF) << 1) + ) + return sext(raw, 13) + + +def imm_u(insn: int) -> int: + return s32(insn & 0xFFFFF000) + + +def imm_j(insn: int) -> int: + raw = ( + ((insn >> 31) << 20) + | (((insn >> 12) & 0xFF) << 12) + | (((insn >> 20) & 1) << 11) + | (((insn >> 21) & 0x3FF) << 1) + ) + return sext(raw, 21) + + +def lookup(insn: int) -> InsnDef | None: + insn &= M32 + for d in INSNS: + if insn & d.mask == d.match: + return d + return None + + +def decode(insn: int) -> Decoded | None: + """Decode a 32-bit word; None if it is not a valid RV32I (+Zicntr read) instruction.""" + insn &= M32 + d = lookup(insn) + if d is None: + return None + rd, rs1, rs2 = (insn >> 7) & 31, (insn >> 15) & 31, (insn >> 20) & 31 + imm = csr = 0 + f = d.fmt + if f == "R": + pass + elif f == "I": + imm, rs2 = imm_i(insn), 0 + elif f == "SH": + imm, rs2 = rs2, 0 + elif f == "S": + imm, rd = imm_s(insn), 0 + elif f == "B": + imm, rd = imm_b(insn), 0 + elif f == "U": + imm, rs1, rs2 = imm_u(insn), 0, 0 + elif f == "J": + imm, rs1, rs2 = imm_j(insn), 0, 0 + elif f == "CSR": + csr, rs2 = (insn >> 20) & 0xFFF, 0 + else: # FENCE, SYS: no architectural operands for this core + rd = rs1 = rs2 = 0 + return Decoded(d.name, f, rd, rs1, rs2, imm, csr, insn) + + +# ---------------------------------------------------------------- encoder + + +def _check_imm(name: str, imm: int, bits: int, align: int = 1) -> None: + lo, hi = -(1 << (bits - 1)), (1 << (bits - 1)) - 1 + if not lo <= imm <= hi or imm % align: + raise ValueError(f"{name}: immediate {imm} out of range/alignment ({bits}-bit, x{align})") + + +def encode(name: str, rd: int = 0, rs1: int = 0, rs2: int = 0, imm: int = 0, csr: int = 0) -> int: + """Encode one instruction. `imm` is signed (U-type: the 20-bit field value << 12 as signed + or unsigned, B/J: byte offsets). For SH the shift amount is `imm`.""" + d = BY_NAME[name] + for r in (rd, rs1, rs2): + if not 0 <= r < 32: + raise ValueError(f"{name}: register {r} out of range") + w, f = d.match, d.fmt + if f == "R": + w |= (rd << 7) | (rs1 << 15) | (rs2 << 20) + elif f == "I": + _check_imm(name, imm, 12) + w |= (rd << 7) | (rs1 << 15) | ((imm & 0xFFF) << 20) + elif f == "SH": + if not 0 <= imm < 32: + raise ValueError(f"{name}: shamt {imm} out of range") + w |= (rd << 7) | (rs1 << 15) | (imm << 20) + elif f == "S": + _check_imm(name, imm, 12) + w |= ((imm & 0x1F) << 7) | (rs1 << 15) | (rs2 << 20) | (((imm >> 5) & 0x7F) << 25) + elif f == "B": + _check_imm(name, imm, 13, 2) + w |= ( + (((imm >> 11) & 1) << 7) + | (((imm >> 1) & 0xF) << 8) + | (rs1 << 15) + | (rs2 << 20) + | (((imm >> 5) & 0x3F) << 25) + | (((imm >> 12) & 1) << 31) + ) + elif f == "U": + w |= (rd << 7) | (imm & 0xFFFFF000) + elif f == "J": + _check_imm(name, imm, 21, 2) + w |= ( + (rd << 7) + | (((imm >> 12) & 0xFF) << 12) + | (((imm >> 11) & 1) << 20) + | (((imm >> 1) & 0x3FF) << 21) + | (((imm >> 20) & 1) << 31) + ) + elif f == "CSR": + w |= (rd << 7) | (rs1 << 15) | ((csr & 0xFFF) << 20) + # FENCE / SYS: fixed encodings (fence = plain `fence` with zero fields) + return w & M32 + + +# ---------------------------------------------------------------- disassembler + + +def disasm(insn: int, pc: int | None = None) -> str: + """ABI-name disassembly. With `pc`, branch/jump targets are shown as absolute addresses.""" + x = decode(insn) + if x is None: + return f".word 0x{insn & M32:08x}" + r = REG_NAMES + n, f = x.name, x.fmt + + def target() -> str: + return f"0x{(pc + x.imm) & M32:x}" if pc is not None else f"{x.imm:+d}" + + if f == "R": + return f"{n} {r[x.rd]}, {r[x.rs1]}, {r[x.rs2]}" + if f == "SH": + return f"{n} {r[x.rd]}, {r[x.rs1]}, {x.imm}" + if f == "I": + if n in LOADS: + return f"{n} {r[x.rd]}, {x.imm}({r[x.rs1]})" + if n == "jalr": + return f"{n} {r[x.rd]}, {x.imm}({r[x.rs1]})" + return f"{n} {r[x.rd]}, {r[x.rs1]}, {x.imm}" + if f == "S": + return f"{n} {r[x.rs2]}, {x.imm}({r[x.rs1]})" + if f == "B": + return f"{n} {r[x.rs1]}, {r[x.rs2]}, {target()}" + if f == "U": + return f"{n} {r[x.rd]}, 0x{(x.imm & M32) >> 12:x}" + if f == "J": + return f"{n} {r[x.rd]}, {target()}" + if f == "CSR": + return f"{n} {r[x.rd]}, {CSR_NAMES.get(x.csr, hex(x.csr))}, {r[x.rs1]}" + return n diff --git a/sim/golden/gen_asm_fixtures.py b/sim/golden/gen_asm_fixtures.py new file mode 100644 index 0000000..512f39a --- /dev/null +++ b/sim/golden/gen_asm_fixtures.py @@ -0,0 +1,335 @@ +"""GNU reference bytes for the web assembler (web/src/lib/assembler.ts). + + cd sim && python -m golden.gen_asm_fixtures # rewrite web/src/lib/__tests__/gnu-asm.json + cd sim && python -m golden.gen_asm_fixtures --check # exit 1 if stale + +Each source is compiled with riscv-none-elf-gcc (-mno-relax, so `call`/`la` keep their auipc+jalr/addi +form) and linked with sw/link.ld, so .text starts at 0 and .data at 0x10000, exactly as the assembler +lays them out. The JSON stores the source and the GNU bytes of .text and of the data span. The TypeScript +test assembles the same source and must reproduce both byte for byte. + +Sources: the web presets (web/src/lib/presets/*.s), the macro programs in sim/programs/*.S (preprocessed +with gcc -E first), and a corpus of snippets that touch every mnemonic, pseudo-instruction and directive. +""" + +from __future__ import annotations + +import json +import re +import subprocess +import sys +import tempfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +OUT = ROOT / "web" / "src" / "lib" / "__tests__" / "gnu-asm.json" +PRESETS = ROOT / "web" / "src" / "lib" / "presets" +PROGRAMS = ROOT / "sim" / "programs" +LINK = ROOT / "sw" / "link.ld" +DATA_BASE = 0x10000 +ARCH = ["-march=rv32i_zicsr_zifencei", "-mabi=ilp32", "-mno-relax"] +GCC = "riscv-none-elf-gcc" + +SNIPPETS: dict[str, str] = { + "alu_register": """ +_start: + li a0, 0x7fffffff + li a1, -3 + add a2, a0, a1 + sub a3, a0, a1 + sll a4, a0, a1 + slt a5, a1, a0 + sltu a6, a1, a0 + xor a7, a0, a1 + srl t0, a1, a0 + sra t1, a1, a0 + or t2, a0, a1 + and t3, a0, a1 +""", + "alu_immediate": """ +_start: + addi a0, zero, -2048 + addi a1, a0, 2047 + slti a2, a0, -1 + sltiu a3, a0, -1 + xori a4, a0, 0x7f0 + ori a5, a0, -16 + andi a6, a0, 0x3c + slli a7, a0, 31 + srli t0, a0, 4 + srai t1, a0, 1 +""", + "loads_stores": """ +_start: + la a0, buf + lb a1, 0(a0) + lbu a2, 1(a0) + lh a3, 2(a0) + lhu a4, -2(a0) + lw a5, 4(a0) + sb a1, 8(a0) + sh a2, 10(a0) + sw a3, 12(a0) +.data +buf: .word 0x80ff7f01, 0xaabbccdd, 0, 0 +""", + "upper_immediates": """ +_start: + lui a0, 0x12345 + lui a1, 0xfffff + lui a2, 0x80000 + auipc a3, 0 + auipc a4, 0x1 +""", + "branches": """ +_start: + li a0, 5 + li a1, -3 + beq a0, a1, fwd + bne a0, a1, fwd + blt a1, a0, fwd + bge a0, a1, fwd + bltu a0, a1, fwd + bgeu a1, a0, fwd + nop +fwd: + beq a0, a0, back +back: + addi a0, a0, -1 + bne a0, zero, back +""", + "jumps": """ +_start: + jal target + jal ra, target + jal zero, target + la t0, target + jalr zero, 0(t0) + jalr ra, t0, 8 + jalr t1, 4(t0) + jalr t2 +target: + jalr zero, 0(ra) +""", + "system_and_counters": """ +_start: + fence + ecall + ebreak + csrrs a0, cycle, zero + csrrs a1, time, zero + csrrs a2, instret, zero + csrrs a3, cycleh, zero + csrrs a4, timeh, zero + csrrs a5, instreth, zero + csrrs a6, 0xc00, zero + csrr a7, instret + rdcycle t0 + rdtime t1 + rdinstret t2 + rdcycleh t3 + rdtimeh t4 + rdinstreth t5 +""", + "pseudo_arithmetic": """ +_start: + nop + mv a0, a1 + not a2, a3 + neg a4, a5 + seqz a6, a7 + snez t0, t1 + sltz t2, t3 + sgtz t4, t5 +""", + "pseudo_branches": """ +_start: + li a0, 4 + beqz a0, t + bnez a0, t + blez a0, t + bgez a0, t + bltz a0, t + bgtz a0, t + bgt a0, a1, t + ble a0, a1, t + bgtu a0, a1, t + bleu a0, a1, t +t: + j t + jr ra + ret +""", + "li_values": """ +_start: + li a0, 0 + li a1, 2047 + li a2, -2048 + li a3, 2048 + li a4, -2049 + li a5, 0x12345000 + li a6, 0x12345678 + li a7, 0x7fffffff + li t0, 0x80000000 + li t1, 0xffffffff + li t2, 0x800 + li t3, 0xfffff800 +""", + "la_and_calls": """ +_start: + la a0, far + call near + tail near + call far +near: + ret +.data +far: .word 1 +""", + "data_directives": """ +_start: + nop +.data + .word 1, -1, 0x7fffffff, 0xffffffff + .half 0x1234, -2, 65535 + .byte 0, 127, 255, 'A', '\\n' + .ascii "ab" + .asciz "cd" + .string "ef\\x21" + .space 3 + .space 2, 0xee + .zero 2 + .align 3 + .word 0xdeadbeef + .balign 8 + .byte 9 +""", + "rodata_and_sections": """ +_start: + la a0, msg + lbu a1, 0(a0) +.section .rodata +msg: .asciz "rodata" +.section .text + nop +""", + "equ_and_expressions": """ +.equ BASE, 0x10000 +.equ COUNT, 4 * 3 + 1 +_start: + li a0, BASE + COUNT + li a1, (1 << 12) - 1 + addi a2, zero, COUNT - 1 + li a3, -(COUNT) + li a4, 0xf0 | 0x0f + li a5, 0xff & ~0x0f + li a6, 7 % 4 + li a7, 'z' - 'a' +.data + .word BASE + 4, end - start_data +start_data: + .byte COUNT +end: +""", + "local_labels": """ +_start: + li a0, 3 +1: addi a0, a0, -1 + bnez a0, 1b + j 2f +2: li a1, 1 + beqz a1, 1f + nop +1: li a2, 2 +""", + "text_alignment": """ +_start: + nop + .byte 1 + .align 2 + nop + .byte 2, 3 + .align 2 + nop +""", +} + + +def _run(args: list[str]) -> bytes: + res = subprocess.run(args, capture_output=True) + if res.returncode != 0: + raise RuntimeError(f"{' '.join(args)} failed:\n{res.stderr.decode(errors='replace')}") + return res.stdout + + +def _sections(elf: Path) -> dict[str, tuple[int, int]]: + out: dict[str, tuple[int, int]] = {} + text = _run([f"riscv-none-elf-readelf", "-SW", str(elf)]).decode() + for line in text.splitlines(): + m = re.search(r"\[\s*\d+\]\s+(\S+)\s+\S+\s+([0-9a-f]+)\s+[0-9a-f]+\s+([0-9a-f]+)", line) + if m and m.group(1).startswith("."): + out[m.group(1)] = (int(m.group(2), 16), int(m.group(3), 16)) + return out + + +def _script(work: Path) -> Path: + """sw/link.ld minus its ALIGN(4) lines: those pad the output, and as/ld do not pad the program's bytes.""" + lines = [ln for ln in LINK.read_text().splitlines() if "ALIGN(4)" not in ln] + out = work / "gen.ld" + out.write_text("\n".join(lines) + "\n") + return out + + +def gnu_bytes(name: str, source: str, work: Path) -> dict: + src = work / f"{re.sub(r'[^A-Za-z0-9_]', '_', name)}.S" + src.write_text(source) + elf = src.with_suffix(".elf") + _run([GCC, *ARCH, "-static", "-nostdlib", "-nostartfiles", "-ffreestanding", + f"-Wl,-T,{_script(work)}", "-Wl,--no-warn-rwx-segments", str(src), "-o", str(elf)]) + secs = _sections(elf) + text = _run(["riscv-none-elf-objcopy", "-O", "binary", "-j", ".text", str(elf), "/dev/stdout"]) if ".text" in secs else b"" + data_end = DATA_BASE + for n, (addr, size) in secs.items(): + if addr >= DATA_BASE and size and n in (".data", ".rodata", ".sdata", ".bss", ".sbss", ".srodata"): + data_end = max(data_end, addr + size) + data = bytearray(data_end - DATA_BASE) + for n, (addr, size) in secs.items(): + if addr < DATA_BASE or not size or n not in (".data", ".rodata", ".sdata", ".srodata"): + continue + blob = _run(["riscv-none-elf-objcopy", "-O", "binary", "-j", n, str(elf), "/dev/stdout"]) + data[addr - DATA_BASE : addr - DATA_BASE + len(blob)] = blob + return {"source": source, "text": text.hex(), "data": bytes(data).hex()} + + +def preprocessed(path: Path) -> str: + return _run(["riscv-none-elf-gcc", "-E", "-P", f"-I{PROGRAMS}", str(path)]).decode() + + +def render() -> str: + sources: dict[str, str] = {} + for p in sorted(PRESETS.glob("*.s")): + sources[f"preset/{p.stem}"] = p.read_text() + for p in sorted(PROGRAMS.glob("*.S")): + sources[f"sim_programs/{p.stem}"] = preprocessed(p) + sources.update({f"snippet/{k}": v for k, v in SNIPPETS.items()}) + with tempfile.TemporaryDirectory(prefix="gnu-asm-") as tmp: + work = Path(tmp) + out = {name: gnu_bytes(name, src, work) for name, src in sources.items()} + return json.dumps(out, indent=1, sort_keys=True) + "\n" + + +def main(argv: list[str]) -> int: + text = render() + if "--check" in argv: + if not OUT.exists() or OUT.read_text() != text: + print(f"{OUT} is stale: run `cd sim && python -m golden.gen_asm_fixtures`", file=sys.stderr) + return 1 + return 0 + OUT.parent.mkdir(parents=True, exist_ok=True) + OUT.write_text(text) + print(f"wrote {OUT}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv[1:])) diff --git a/sim/golden/gen_isa_ts.py b/sim/golden/gen_isa_ts.py new file mode 100644 index 0000000..2a47d60 --- /dev/null +++ b/sim/golden/gen_isa_ts.py @@ -0,0 +1,66 @@ +"""Generate web/src/lib/isa.ts from decode.py so the TypeScript assembler/disassembler and the +Python golden model share one opcode table. + + python -m golden.gen_isa_ts # rewrite the file + python -m golden.gen_isa_ts --check # exit 1 if it is stale (used by pytest/CI) +""" + +from __future__ import annotations + +import sys +from pathlib import Path + +from golden.decode import CSR_NAMES, FORMATS, INSNS, REG_NAMES + +OUT = Path(__file__).resolve().parents[2] / "web" / "src" / "lib" / "isa.ts" + + +def render() -> str: + rows = "\n".join( + f' {{ name: "{d.name}", fmt: "{d.fmt}", mask: 0x{d.mask:08x}, match: 0x{d.match:08x} }},' + for d in INSNS + ) + regs = ", ".join(f'"{r}"' for r in REG_NAMES) + csrs = "\n".join(f' 0x{k:03x}: "{v}",' for k, v in CSR_NAMES.items()) + fmts = " | ".join(f'"{f}"' for f in FORMATS) + return f"""// GENERATED by sim/golden/gen_isa_ts.py from sim/golden/decode.py - do not edit. +// Regenerate: (cd sim && python -m golden.gen_isa_ts) + +export type InsnFormat = {fmts}; + +export interface InsnDef {{ + name: string; + fmt: InsnFormat; + /** An instruction word w matches when (w & mask) === match. */ + mask: number; + match: number; +}} + +export const INSNS: readonly InsnDef[] = [ +{rows} +]; + +/** ABI register names, indexed by register number. */ +export const REG_NAMES: readonly string[] = [{regs}]; + +/** Implemented read-only counter CSRs (Zicntr subset). */ +export const CSR_NAMES: Readonly> = {{ +{csrs} +}}; +""" + + +def main(argv: list[str]) -> int: + text = render() + if "--check" in argv: + if not OUT.exists() or OUT.read_text() != text: + print(f"{OUT} is stale: run `python -m golden.gen_isa_ts` from sim/", file=sys.stderr) + return 1 + return 0 + OUT.write_text(text) + print(f"wrote {OUT}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv[1:])) diff --git a/sim/golden/hazard_cases.py b/sim/golden/hazard_cases.py new file mode 100644 index 0000000..e9055a4 --- /dev/null +++ b/sim/golden/hazard_cases.py @@ -0,0 +1,37 @@ +"""Hand-derived hazard cases shared by the ISS-only model tests and the RTL tests. + +Each case is (instructions, extra cycles with forwarding on, extra cycles with forwarding off), where +"extra" means cycles beyond the ideal N + 4 (N instructions + pipeline fill). The numbers were derived +on paper from the stage diagrams in docs/ARCHITECTURE.md, not from the RTL. SETUP puts the base +register 3+ instructions ahead of the loads so the only hazard in each case is the one under test. +""" + +from golden.decode import encode as e + +DMEM = (0x10008).to_bytes(4, "little") + bytes(60) # word 0 holds a valid pointer into DMEM + + +ADD = e("addi", 1, 0, imm=5) +NOP = e("addi", 9, 0, imm=0) +SETUP = [e("lui", 3, imm=0x10000), NOP, NOP, NOP] +CASES = { + "independent": ([ADD, e("addi", 2, 0, imm=6)], 0, 0), + "alu->alu dist 1": ([ADD, e("addi", 2, 1, imm=1)], 0, 2), + "alu->alu dist 2": ([ADD, NOP, e("addi", 2, 1, imm=1)], 0, 1), + "alu->alu dist 3": ([ADD, NOP, NOP, e("addi", 2, 1, imm=1)], 0, 0), + "load->use dist 1": (SETUP + [e("lw", 1, 3, imm=0), e("addi", 2, 1, imm=1)], 1, 2), + "load->use dist 2": (SETUP + [e("lw", 1, 3, imm=0), NOP, e("addi", 2, 1, imm=1)], 0, 1), + "load->use dist 3": (SETUP + [e("lw", 1, 3, imm=0), NOP, NOP, e("addi", 2, 1, imm=1)], 0, 0), + "load->store-data dist 1": (SETUP + [e("lw", 1, 3, imm=0), e("sw", 0, 3, 1, imm=4)], 1, 2), + "load->store-address dist 1": (SETUP + [e("lw", 1, 3, imm=0), e("sw", 0, 1, 3, imm=4)], 1, 2), + "csr->use dist 1": ([e("csrrs", 1, 0, csr=0xC02), e("addi", 2, 1, imm=1)], 1, 2), + "jal link->use dist 1": ([e("jal", 1, imm=4), e("addi", 2, 1, imm=1)], 2, 2), # flush gap subsumes the stall + "both operands late": (SETUP + [e("lw", 1, 3, imm=0), e("lw", 2, 3, imm=4), e("add", 4, 1, 2)], 1, 2), + "taken jal": ([e("jal", 0, imm=8), e("addi", 7, 0, imm=1), e("addi", 8, 0, imm=2)], 2, 2), + "taken branch": ([ADD, NOP, NOP, e("beq", 0, 1, 1, imm=8), e("addi", 7, 0, imm=1), e("addi", 8, 0, imm=2)], 2, 2), + "taken branch to pc+4": ([ADD, NOP, NOP, e("beq", 0, 1, 1, imm=4), e("addi", 7, 0, imm=1)], 2, 2), + "not-taken branch": ([ADD, NOP, NOP, e("bne", 0, 1, 1, imm=8)], 0, 0), + "writes x0 never hazard": ([e("addi", 0, 0, imm=5), e("addi", 2, 0, imm=1)], 0, 0), +} + + diff --git a/sim/golden/hazard_free.py b/sim/golden/hazard_free.py new file mode 100644 index 0000000..3438ed0 --- /dev/null +++ b/sim/golden/hazard_free.py @@ -0,0 +1,302 @@ +"""Programs the Phase 3 pipeline (no forwarding, no load-use stall) can run correctly. + +Every program is built with Prog(pad=2) except `mem_raw_adjacent`, which is hand-scheduled. They are +checked twice: on the ISS alone (test_progbuild.py: result + no dynamic RAW closer than 3), and on +the RTL in lockstep with the ISS (sim/test_pipe_lockstep.py). Once Phase 4 adds forwarding the same +programs keep working (a faster core still runs hazard-free code correctly). +""" + +from __future__ import annotations + +import random + +from golden.decode import BY_NAME +from golden.iss import CONSOLE_ADDR, CYCLE_ADDR, DMEM_BASE +from golden.progbuild import Prog + +R_OPS = ["add", "sub", "sll", "slt", "sltu", "xor", "srl", "sra", "or", "and"] +I_OPS = ["addi", "slti", "sltiu", "xori", "ori", "andi"] +SH_OPS = ["slli", "srli", "srai"] +BRANCH_OPS = ["beq", "bne", "blt", "bge", "bltu", "bgeu"] +VALUES = [0, 1, 0xFFFFFFFF, 0x7FFFFFFF, 0x80000000, 0x12345678, 0xDEADBEEF, 0x000000FF, 0xFFFF8001] + + +def alu_zoo() -> Prog: + """Every ALU operation on awkward operands; each result is stored so it is also seen as write data.""" + p = Prog() + out = p.space(4 * 64) + p.li(1, 0x80000001) + p.li(2, 0xFFFFFFF3) + p.li(3, out) + k = 0 + for op in R_OPS: + p.op(op, 4, 1, 2) + p.sw(4, 3, 4 * k) + k += 1 + for op in I_OPS: + for imm in (-1, 0x7FF, -2048): + p.op(op, 5, 1, imm) + p.sw(5, 3, 4 * k) + k += 1 + for op in SH_OPS: + for sh in (0, 1, 31): + p.op(op, 6, 2, sh) + p.sw(6, 3, 4 * k) + k += 1 + p.lui(7, 0xFFFFF) + p.auipc(8, 0x1) + p.sw(7, 3, 4 * k) + p.sw(8, 3, 4 * k + 4) + return p.exit() + + +def mem_zoo() -> Prog: + """Every load/store size at every offset, including misaligned ones that get forced into alignment.""" + p = Prog() + buf = p.word(0x80FF7F01, 0xAABBCCDD, 0x00000000, 0xFFFFFFFF) + p.li(1, buf) + for i, op in enumerate(["lb", "lbu", "lh", "lhu", "lw"]): + for off in range(8): + p.op(op, 2, 1, off) + p.sw(2, 1, 32 + 4 * (i * 8 + off) % 64) + p.li(3, 0x11223344) + for off in range(8): + p.sb(3, 1, 16 + off) + p.lw(4, 1, 16) + p.lw(5, 1, 20) + for off in (0, 2, 5): + p.sh(3, 1, 24 + off) + p.lw(6, 1, 24) + p.lw(7, 1, 28) + p.sw(3, 1, 9) # misaligned word store -> forced to offset 8 + p.lw(8, 1, 8) + return p.exit() + + +def mem_raw_adjacent() -> Prog: + """Hand-scheduled (pad=0): a load directly after a store to the same word. The store writes DMEM + in MEM at the same edge the load reads it in EX, so this only works because mem_sync is write-first.""" + p = Prog(pad=0) + buf = p.space(64) + p.li(1, buf) + p.li(2, 0xA1B2C3D4) + p.li(9, 0x5A) + p.nop(3) + p.sw(2, 1, 0).lw(3, 1, 0).nop(3) # word store, word load + p.sb(2, 1, 5).lw(4, 1, 4).nop(3) # byte store merged into a later word read + p.sh(2, 1, 10).lhu(5, 1, 10).nop(3) + p.sw(2, 1, 16).sw(9, 1, 20).lw(6, 1, 16).lw(7, 1, 20).nop(3) # back-to-back stores and loads + p.sb(9, 1, 33).sb(9, 1, 34).lw(8, 1, 32).nop(3) # two byte stores then a word load + p.sw(3, 1, 40).nop(3) # consume an earlier load result + p.sw(8, 1, 44).nop(3) + p.exit(1) + return p + + +def branches() -> Prog: + """All six conditions, taken and not taken, forward and backward; x10 counts the taken paths.""" + p = Prog() + p.li(1, 5) + p.li(2, 0xFFFFFFFD) # -3 signed, huge unsigned + p.li(3, 5) + p.li(10, 0) + n = 0 + for op in BRANCH_OPS: + for a, b in ((1, 3), (1, 2), (2, 1)): + p.op(op, a, b, f"t{n}") + p.addi(10, 10, 100) # executes only when NOT taken + p.label(f"t{n}") + p.addi(10, 10, 1) + n += 1 + p.li(4, 7) # backward loop: sum 7+6+...+1 + p.li(5, 0) + p.label("loop") + p.add(5, 5, 4) + p.addi(4, 4, -1) + p.bne(4, 0, "loop") + p.li(6, DMEM_BASE) + p.sw(5, 6, 0) + p.sw(10, 6, 4) + return p.exit() + + +def calls() -> Prog: + """jal/jalr: nested calls, return via ra, a computed jump, and jalr clearing bit 0 of its target.""" + p = Prog() + out = p.space(32) + p.li(20, out) + p.li(5, 3) + p.jal(1, "double") + p.sw(5, 20, 0) + p.jal(1, "double") + p.jal(1, "double") + p.sw(5, 20, 4) + p.li_label(6, "far") + p.jalr(2, 6, 0) # indirect jump with link + p.addi(7, 0, 111) # skipped + p.label("far") + p.nop(2) # the jump's padding was flushed, so the link register (x2) is only 1 instruction old here + p.sw(2, 20, 8) + p.li_label(6, "odd", 1) # odd target: bit 0 must be ignored + p.jalr(0, 6, 0) + p.addi(7, 0, 222) # skipped + p.label("odd") + p.sw(7, 20, 12) + p.jal(0, "end") # unconditional forward jump with no link + p.addi(7, 0, 333) # skipped + p.label("double") # x5 *= 2 then return; nested call through x3 + p.add(5, 5, 5) + p.jalr(0, 1, 0) + p.label("end") + return p.exit() + + +def console_and_csr() -> Prog: + """MMIO: print text through CONSOLE, read instret/cycle CSRs and the CYCLE register.""" + p = Prog() + out = p.space(16) + p.li(20, out) + for ch in "Hi, pipe!\n": + p.putc(ch) + p.csrrs(21, 0xC02) # instret: exact, unmasked in lockstep + p.sw(21, 20, 0) + p.csrrs(22, 0xC00) # cycle: timing-dependent, masked + p.csrrs(23, 0xC01) + p.csrrs(24, 0xC82) # instreth: zero + p.sw(24, 20, 4) + p.li(25, CYCLE_ADDR) + p.lw(26, 25, 0) + p.fence() + p.putc("!") + return p.exit() + + +def fib() -> Prog: + p = Prog() + out = p.space(4) + p.li(5, 20) + p.li(6, 0) + p.li(7, 1) + p.label("loop") + p.beq(5, 0, "end") + p.add(28, 6, 7) + p.addi(6, 7, 0) + p.addi(7, 28, 0) + p.addi(5, 5, -1) + p.jal(0, "loop") + p.label("end") + p.li(8, out) + p.sw(6, 8, 0) # fib(20) = 6765 + return p.exit() + + +def bubble_sort() -> Prog: + p = Prog() + base = p.word(42, 7, 99, 3, 18, 64, 1, 27) + n = 8 + p.li(8, base) + p.li(18, n - 1) + p.label("outer") + p.bge(0, 18, "done") + p.li(5, 0) + p.addi(6, 8, 0) + p.label("inner") + p.bge(5, 18, "next") + p.lw(11, 6, 0) + p.lw(12, 6, 4) + p.bge(12, 11, "noswap") + p.sw(12, 6, 0) + p.sw(11, 6, 4) + p.label("noswap") + p.addi(5, 5, 1) + p.addi(6, 6, 4) + p.jal(0, "inner") + p.label("next") + p.addi(18, 18, -1) + p.jal(0, "outer") + p.label("done") + return p.exit() + + +def memcpy_sum() -> Prog: + p = Prog() + src = p.word(*[(i * 0x01010101) ^ 0xA5A5A5A5 for i in range(12)]) + dst = p.space(48) + p.li(1, dst) + p.li(2, src) + p.li(3, 48) # byte copy loop + p.label("copy") + p.beq(3, 0, "sum") + p.lbu(4, 2, 0) + p.sb(4, 1, 0) + p.addi(1, 1, 1) + p.addi(2, 2, 1) + p.addi(3, 3, -1) + p.jal(0, "copy") + p.label("sum") + p.li(1, dst) + p.li(3, 12) + p.li(9, 0) + p.label("acc") + p.beq(3, 0, "fin") + p.lw(4, 1, 0) + p.add(9, 9, 4) + p.addi(1, 1, 4) + p.addi(3, 3, -1) + p.jal(0, "acc") + p.label("fin") + p.li(1, dst) + p.sw(9, 1, 0) + return p.exit() + + +def random_program(seed: int, n: int = 60) -> Prog: + """Random straight-line-ish code with forward-only branches (always terminates), padded hazard-free. + x1 holds a DMEM base that is never overwritten; x2..x15 are scratch.""" + rng = random.Random(seed) + p = Prog() + buf = p.space(256) + p.li(1, buf) + for r in range(2, 16): + p.li(r, rng.choice(VALUES) ^ rng.getrandbits(8)) + labels_open: list[tuple[int, str]] = [] + for i in range(n): + for pending in [x for x in labels_open if x[0] <= i]: + p.label(pending[1]) + labels_open.remove(pending) + rd, a, b = (rng.randrange(2, 16) for _ in range(3)) + kind = rng.choices(["r", "i", "sh", "lui", "load", "store", "branch"], [5, 4, 2, 1, 3, 3, 2])[0] + if kind == "r": + p.op(rng.choice(R_OPS), rd, a, b) + elif kind == "i": + p.op(rng.choice(I_OPS), rd, a, rng.randint(-2048, 2047)) + elif kind == "sh": + p.op(rng.choice(SH_OPS), rd, a, rng.randrange(32)) + elif kind == "lui": + p.op(rng.choice(["lui", "auipc"]), rd, rng.getrandbits(20)) + elif kind == "load": + p.op(rng.choice(["lb", "lbu", "lh", "lhu", "lw"]), rd, 1, rng.randrange(0, 250)) + elif kind == "store": + p.op(rng.choice(["sb", "sh", "sw"]), a, 1, rng.randrange(0, 250)) + else: + name = f"L{i}" + labels_open.append((i + rng.randint(2, 4), name)) # skip >= 2 slots: never a +4 branch + p.op(rng.choice(BRANCH_OPS), a, b, name) + for _, name in labels_open: + p.label(name) + return p.exit() + + +PROGRAMS = { + "alu_zoo": alu_zoo, + "mem_zoo": mem_zoo, + "mem_raw_adjacent": mem_raw_adjacent, + "branches": branches, + "calls": calls, + "console_and_csr": console_and_csr, + "fib": fib, + "bubble_sort": bubble_sort, + "memcpy_sum": memcpy_sum, +} + +assert all(n in BY_NAME for n in R_OPS + I_OPS + SH_OPS + BRANCH_OPS) diff --git a/sim/golden/hazard_random.py b/sim/golden/hazard_random.py new file mode 100644 index 0000000..ffc2d18 --- /dev/null +++ b/sim/golden/hazard_random.py @@ -0,0 +1,64 @@ +"""Hazard-heavy random programs for Phase 4: dense code (pad=0), few registers so nearly every +instruction depends on a recent one, loads feeding uses, store->load on the same word, forward branches, +jumps, indirect jumps and instret reads. Always terminates (branches only go forward).""" + +from __future__ import annotations + +import random + +from golden.hazard_free import BRANCH_OPS, I_OPS, R_OPS, SH_OPS +from golden.progbuild import Prog + +POOL = [1, 2, 3, 4, 5] # small register pool => constant dependencies +BASE = 8 # holds the DMEM base address; never written after setup + + +def random_hazard_program(seed: int, n: int = 70) -> Prog: + rng = random.Random(seed) + p = Prog(pad=0) + buf = p.space(128) + p.li(BASE, buf) + for r in POOL: + p.li(r, rng.getrandbits(32)) + p.nop(3) + pending: list[tuple[int, str]] = [] # (emit label before instruction #, name) + for i in range(n): + for lab in [x for x in pending if x[0] <= i]: + p.label(lab[1]) + pending.remove(lab) + rd, a, b = (rng.choice(POOL) for _ in range(3)) + kind = rng.choices( + ["r", "i", "sh", "lui", "load", "store", "branch", "jal", "jalr", "csr"], + [6, 5, 2, 1, 5, 4, 3, 1, 1, 1], + )[0] + if kind == "r": + p.op(rng.choice(R_OPS), rd, a, b) + elif kind == "i": + p.op(rng.choice(I_OPS), rd, a, rng.randint(-2048, 2047)) + elif kind == "sh": + p.op(rng.choice(SH_OPS), rd, a, rng.randrange(32)) + elif kind == "lui": + p.op(rng.choice(["lui", "auipc"]), rd, rng.getrandbits(20)) + elif kind == "load": + p.op(rng.choice(["lb", "lbu", "lh", "lhu", "lw"]), rd, BASE, rng.randrange(0, 124)) + elif kind == "store": + p.op(rng.choice(["sb", "sh", "sw"]), a, BASE, rng.randrange(0, 124)) + elif kind == "branch": + name = f"B{i}" + pending.append((i + rng.randint(2, 4), name)) # >= 2 ahead: a taken branch is never +4 + p.op(rng.choice(BRANCH_OPS), a, b, name) + elif kind == "jal": + name = f"J{i}" + pending.append((i + rng.randint(2, 4), name)) + p.jal(rng.choice([0, rd]), name) + elif kind == "jalr": + name = f"R{i}" + pending.append((i + rng.randint(3, 5), name)) + p.li_label(6, name, rng.choice([0, 1])) # x6 is the jump register: odd targets exercise bit 0 + p.jalr(rng.choice([0, rd]), 6, 0) + else: + p.csrrs(rd, 0xC02) # instret: exact in the ISS, so safe to consume + for _, name in pending: + p.label(name) + p.nop(3) + return p.exit() diff --git a/sim/golden/iss.py b/sim/golden/iss.py new file mode 100644 index 0000000..57114c2 --- /dev/null +++ b/sim/golden/iss.py @@ -0,0 +1,320 @@ +"""Golden RV32I instruction-set simulator for TinyCPU. + +This is the reference the RTL is checked against (docs/PLAN.md section 5). It models the +architecture, not the pipeline: one instruction retires per step() and produces one +RVFI-lite `Retire` record. Keep it small and obviously correct. + +Memory map (docs/PLAN.md section 2.1), Harvard: + IMEM 0x0000_0000 4 KiB fetch only (the data port cannot read or write it) + DMEM 0x0001_0000 4 KiB byte-addressable, LB/LH/LW/LBU/LHU/SB/SH/SW + CONSOLE 0x0002_0000 write: lane-0 byte is sent to the console + TOHOST 0x0002_0004 SW only: halts the core. 1 = PASS, anything else = FAIL code + CYCLE 0x0002_0008 read-only cycle counter + +Deliberate behaviours the RTL must match: + * Misaligned loads/stores are NOT supported: the address is forced into alignment and the + retire record carries misaligned=True. Misaligned jump/branch targets raise. + * ecall / ebreak / a TOHOST write halt the core; the halting instruction still retires. + * `Retire.taken` is informational (not compared in lockstep): it lets the pipeline timing model + see a taken branch whose target happens to be pc+4, which pc_wdata alone cannot reveal. + * Cycle-dependent values (CYCLE MMIO, rdcycle/rdtime) return the retired-instruction count + here, which is NOT what a pipelined core returns. The lockstep harness masks the + destination value of those instructions (see `Retire.cycle_dependent`). +""" + +from __future__ import annotations + +from dataclasses import asdict, dataclass +from pathlib import Path +from typing import Callable + +from golden.decode import ( + BRANCHES, + CSR_NAMES, + LOADS, + M32, + STORES, + Decoded, + decode, + disasm, + s32, +) + +IMEM_BASE, IMEM_SIZE = 0x0000_0000, 4096 +DMEM_BASE, DMEM_SIZE = 0x0001_0000, 4096 +CONSOLE_ADDR = 0x0002_0000 +TOHOST_ADDR = 0x0002_0004 +CYCLE_ADDR = 0x0002_0008 +MMIO_BASE, MMIO_END = CONSOLE_ADDR, CYCLE_ADDR + 4 + +PASS_CODE = 1 + + +class IssError(Exception): + """The program did something the core does not define (trap-less design: this is fatal).""" + + def __init__(self, reason: str, pc: int, insn: int | None = None): + self.reason, self.pc, self.insn = reason, pc, insn + detail = f" insn=0x{insn:08x} ({disasm(insn, pc)})" if insn is not None else "" + super().__init__(f"{reason} at pc=0x{pc:08x}{detail}") + + +class IllegalInstruction(IssError): + pass + + +class BusFault(IssError): + pass + + +class MisalignedFetch(IssError): + pass + + +@dataclass +class Retire: + """RVFI-lite retire record (docs/PLAN.md section 2.4). + + mem_addr is the word-aligned address; masks are byte lanes within that word; mem_rdata and + mem_wdata are lane-positioned 32-bit words (unselected lanes are zero), exactly what the + LSU datapath carries. + """ + + order: int + pc: int + insn: int + rd_addr: int # 0 when no register is written (including writes to x0) + rd_wdata: int # 0 when rd_addr == 0 + pc_wdata: int # address of the next instruction + mem_addr: int = 0 + mem_rmask: int = 0 + mem_wmask: int = 0 + mem_rdata: int = 0 + mem_wdata: int = 0 + misaligned: bool = False + halt: bool = False + halt_reason: str = "" # "", "ecall", "ebreak", "tohost"; informational, not compared + cycle_dependent: bool = False # rd_wdata/mem_rdata depend on timing; lockstep must mask them + taken: bool = False # a taken branch or any jump: the pipeline flushes (even when the target is pc+4) + + def asdict(self) -> dict: + return asdict(self) + + def __str__(self) -> str: + s = f"#{self.order:<5} pc=0x{self.pc:08x} {disasm(self.insn, self.pc):<28}" + if self.rd_addr: + s += f" x{self.rd_addr}=0x{self.rd_wdata:08x}" + if self.mem_rmask: + s += f" R[0x{self.mem_addr:08x}/{self.mem_rmask:04b}]=0x{self.mem_rdata:08x}" + if self.mem_wmask: + s += f" W[0x{self.mem_addr:08x}/{self.mem_wmask:04b}]=0x{self.mem_wdata:08x}" + return s + (f" HALT({self.halt_reason})" if self.halt else "") + + +_SIZE = {"lb": 1, "lbu": 1, "sb": 1, "lh": 2, "lhu": 2, "sh": 2, "lw": 4, "sw": 4} + + +class Iss: + def __init__( + self, + imem: bytes = b"", + dmem: bytes = b"", + on_console: Callable[[int], None] | None = None, + ): + self.imem = bytearray(IMEM_SIZE) + self.dmem = bytearray(DMEM_SIZE) + self.on_console = on_console + self.console = bytearray() + self.regs = [0] * 32 + self.pc = IMEM_BASE + self.instret = 0 + self.halted = False + self.halt_reason = "" + self.tohost: int | None = None + self.load(imem, dmem) + + # ---------------------------------------------------------------- loading + def load(self, imem: bytes, dmem: bytes) -> None: + if len(imem) > IMEM_SIZE or len(dmem) > DMEM_SIZE: + raise ValueError(f"image too large: imem {len(imem)}/{IMEM_SIZE}, dmem {len(dmem)}/{DMEM_SIZE}") + self.imem[:] = bytes(imem).ljust(IMEM_SIZE, b"\0") + self.dmem[:] = bytes(dmem).ljust(DMEM_SIZE, b"\0") + + @classmethod + def from_files(cls, imem_path: str | Path, dmem_path: str | Path | None = None, **kw) -> "Iss": + dmem = Path(dmem_path).read_bytes() if dmem_path and Path(dmem_path).exists() else b"" + return cls(Path(imem_path).read_bytes(), dmem, **kw) + + @classmethod + def from_build(cls, stem: str | Path, **kw) -> "Iss": + """Load `.imem.bin` + `.dmem.bin` as produced by sw/Makefile.""" + stem = str(stem) + return cls.from_files(stem + ".imem.bin", stem + ".dmem.bin", **kw) + + # ---------------------------------------------------------------- results + @property + def passed(self) -> bool: + return self.tohost == PASS_CODE + + @property + def console_text(self) -> str: + return self.console.decode("latin-1") + + # ---------------------------------------------------------------- memory + def _fetch(self, pc: int) -> int: + if pc & 3: + raise MisalignedFetch("misaligned fetch", pc) + if not IMEM_BASE <= pc < IMEM_BASE + IMEM_SIZE: + raise BusFault("fetch outside IMEM", pc) + return int.from_bytes(self.imem[pc : pc + 4], "little") + + def _load(self, name: str, addr: int, pc: int, insn: int) -> tuple[int, int, int, int, bool]: + """-> (value, word_addr, rmask, lane_positioned_rdata, misaligned).""" + size = _SIZE[name] + aligned = addr & ~(size - 1) + word_addr, off = aligned & ~3, aligned & 3 + mask = ((1 << size) - 1) << off + if DMEM_BASE <= word_addr < DMEM_BASE + DMEM_SIZE: + o = word_addr - DMEM_BASE + word = int.from_bytes(self.dmem[o : o + 4], "little") + elif word_addr == CYCLE_ADDR: + word = self.instret & M32 + else: + raise BusFault(f"load from unmapped/unreadable address 0x{addr:08x}", pc, insn) + lanes = word & sum(0xFF << (8 * i) for i in range(4) if mask >> i & 1) + raw = (lanes >> (8 * off)) & ((1 << (8 * size)) - 1) + if name in ("lb", "lh"): + raw = (raw - (1 << (8 * size))) & M32 if raw >> (8 * size - 1) else raw + return raw, word_addr, mask, lanes, aligned != addr + + def _store(self, name: str, addr: int, value: int, pc: int, insn: int) -> tuple[int, int, int, bool]: + """-> (word_addr, wmask, lane_positioned_wdata, misaligned).""" + size = _SIZE[name] + aligned = addr & ~(size - 1) + word_addr, off = aligned & ~3, aligned & 3 + mask = ((1 << size) - 1) << off + wdata = ((value & ((1 << (8 * size)) - 1)) << (8 * off)) & M32 + if DMEM_BASE <= word_addr < DMEM_BASE + DMEM_SIZE: + o = word_addr - DMEM_BASE + for i in range(4): + if mask >> i & 1: + self.dmem[o + i] = (wdata >> (8 * i)) & 0xFF + elif word_addr == CONSOLE_ADDR: + if mask & 1: + ch = wdata & 0xFF + self.console.append(ch) + if self.on_console: + self.on_console(ch) + elif word_addr == TOHOST_ADDR: + if mask != 0xF: + raise BusFault("TOHOST accepts word stores only", pc, insn) + self.tohost = wdata + self.halted, self.halt_reason = True, "tohost" + else: + raise BusFault(f"store to unmapped/read-only address 0x{addr:08x}", pc, insn) + return word_addr, mask, wdata, aligned != addr + + # ---------------------------------------------------------------- execute + def step(self) -> Retire: + if self.halted: + raise RuntimeError("core is halted") + pc = self.pc + insn = self._fetch(pc) + x = decode(insn) + if x is None: + raise IllegalInstruction("illegal instruction", pc, insn) + r = self._execute(x, pc) + self.pc = r.pc_wdata + self.instret += 1 + return r + + def _execute(self, x: Decoded, pc: int) -> Retire: + regs, n = self.regs, x.name + a, b = regs[x.rs1], regs[x.rs2] + nxt = (pc + 4) & M32 + rd_val: int | None = None + ret = Retire(self.instret, pc, x.insn, 0, 0, nxt) + + if n == "lui": + rd_val = x.imm & M32 + elif n == "auipc": + rd_val = (pc + x.imm) & M32 + elif n == "jal": + rd_val, nxt = nxt, (pc + x.imm) & M32 + ret.taken = True + elif n == "jalr": + rd_val, nxt = nxt, ((a + x.imm) & M32) & ~1 + ret.taken = True + elif n in BRANCHES: + taken = { + "beq": a == b, + "bne": a != b, + "blt": s32(a) < s32(b), + "bge": s32(a) >= s32(b), + "bltu": a < b, + "bgeu": a >= b, + }[n] + if taken: + nxt = (pc + x.imm) & M32 + ret.taken = True + elif n in LOADS: + val, ret.mem_addr, ret.mem_rmask, ret.mem_rdata, ret.misaligned = self._load( + n, (a + x.imm) & M32, pc, x.insn + ) + ret.cycle_dependent = ret.mem_addr == CYCLE_ADDR + rd_val = val + elif n in STORES: + ret.mem_addr, ret.mem_wmask, ret.mem_wdata, ret.misaligned = self._store( + n, (a + x.imm) & M32, b, pc, x.insn + ) + if self.halted: + ret.halt, ret.halt_reason = True, "tohost" + elif n in ("addi", "slti", "sltiu", "xori", "ori", "andi", "slli", "srli", "srai"): + rd_val = _alu(n[:-1] if n != "sltiu" else "sltu", a, x.imm & M32) + elif n in ("add", "sub", "sll", "slt", "sltu", "xor", "srl", "sra", "or", "and"): + rd_val = _alu(n, a, b) + elif n == "fence": + pass + elif n in ("ecall", "ebreak"): + self.halted, self.halt_reason = True, n + ret.halt, ret.halt_reason = True, n + elif n == "csrrs": + if x.rs1 != 0 or x.csr not in CSR_NAMES: + raise IllegalInstruction("only read-only Zicntr csrrs (rs1=x0) is implemented", pc, x.insn) + rd_val = self.instret & M32 if x.csr in (0xC00, 0xC01, 0xC02) else 0 + ret.cycle_dependent = x.csr in (0xC00, 0xC01) + else: # pragma: no cover - decode() only returns names handled above + raise IllegalInstruction(f"unhandled {n}", pc, x.insn) + + if nxt & 3: + raise MisalignedFetch(f"misaligned jump/branch target 0x{nxt:08x}", pc, x.insn) + if rd_val is not None and x.rd != 0: + regs[x.rd] = rd_val & M32 + ret.rd_addr, ret.rd_wdata = x.rd, rd_val & M32 + ret.pc_wdata = nxt + return ret + + def run(self, max_steps: int = 1_000_000) -> list[Retire]: + """Run to halt; returns every retire record. Raises TimeoutError past max_steps.""" + trace: list[Retire] = [] + while not self.halted: + if len(trace) >= max_steps: + raise TimeoutError(f"no halt after {max_steps} instructions (pc=0x{self.pc:08x})") + trace.append(self.step()) + return trace + + +def _alu(op: str, a: int, b: int) -> int: + sh = b & 31 + return { + "add": (a + b) & M32, + "sub": (a - b) & M32, + "sll": (a << sh) & M32, + "slt": int(s32(a) < s32(b)), + "sltu": int(a < b), + "xor": a ^ b, + "srl": a >> sh, + "sra": (s32(a) >> sh) & M32, + "or": a | b, + "and": a & b, + }[op] diff --git a/sim/golden/perf.py b/sim/golden/perf.py new file mode 100644 index 0000000..a6782e9 --- /dev/null +++ b/sim/golden/perf.py @@ -0,0 +1,74 @@ +"""Pinned performance baseline: instructions, and cycles per core/mode, for the example programs. + + python -m golden.perf # print the table + python -m golden.perf --update # rewrite sim/perf_baseline.json (do this deliberately) + python -m golden.perf --check # exit 1 if the baseline is stale (pytest does this too) + +Cycles come from the ISS retire trace and the independent pipeline model. The RTL lockstep suites +assert RTL cycles == model for every one of these programs, so the baseline is pinned against hardware. +""" + +from __future__ import annotations + +import json +import sys +from pathlib import Path + +from golden.hazard_free import PROGRAMS +from golden.iss import Iss +from golden.pipemodel import pipe_cycles + +ROOT = Path(__file__).resolve().parents[2] +BASELINE = ROOT / "sim" / "perf_baseline.json" +SW_PROGRAMS = ["hello", "fib", "sort", "memcpy", "primes"] + + +def sources() -> dict[str, tuple[bytes, bytes]]: + out = {} + for name in SW_PROGRAMS: + stem = ROOT / "sw" / "build" / "programs" / name + if stem.with_suffix(".imem.bin").exists(): + out[name] = (Path(str(stem) + ".imem.bin").read_bytes(), Path(str(stem) + ".dmem.bin").read_bytes()) + for name, build in PROGRAMS.items(): + out[f"hazard_free/{name}"] = build().build() + return out + + +def measure(imem: bytes, dmem: bytes) -> dict[str, int]: + iss = Iss(imem, dmem) + trace = iss.run(2_000_000) + n = len(trace) + return {"instret": n, "single": n, "pipe_fwd": pipe_cycles(trace, True), "pipe_nofwd": pipe_cycles(trace, False)} + + +def measure_all() -> dict[str, dict[str, int]]: + return {name: measure(*img) for name, img in sorted(sources().items())} + + +def table(data) -> str: + head = f"{'program':<28}{'instrs':>8}{'single':>8}{'pipe+fwd':>10}{'CPI':>7}{'pipe-fwd':>10}{'CPI':>7}" + rows = [head, "-" * len(head)] + for name, d in data.items(): + rows.append(f"{name:<28}{d['instret']:>8}{d['single']:>8}{d['pipe_fwd']:>10}" + f"{d['pipe_fwd'] / d['instret']:>7.2f}{d['pipe_nofwd']:>10}{d['pipe_nofwd'] / d['instret']:>7.2f}") + return "\n".join(rows) + + +def main(argv: list[str]) -> int: + data = measure_all() + if "--update" in argv: + BASELINE.write_text(json.dumps(data, indent=2) + "\n") + print(f"wrote {BASELINE}") + elif "--check" in argv: + pinned = json.loads(BASELINE.read_text()) + missing = [k for k in data if k not in pinned] + changed = {k: (pinned[k], data[k]) for k in data if k in pinned and pinned[k] != data[k]} + if missing or changed: + print(f"perf baseline is stale: missing={missing} changed={changed}", file=sys.stderr) + return 1 + print(table(data)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv[1:])) diff --git a/sim/golden/pipemodel.py b/sim/golden/pipemodel.py new file mode 100644 index 0000000..e0fc28a --- /dev/null +++ b/sim/golden/pipemodel.py @@ -0,0 +1,76 @@ +"""Independent timing model of the 5-stage pipeline, computed from an ISS retire trace. + +This is the oracle for the RTL's cycle counts (Phase 4). It knows nothing about the RTL - only the +pipeline's rules - so agreement is real evidence and not the RTL checking itself. + +Let T(i) be the cycle in which instruction i is in EX (IF is T-2, ID is T-1, MEM is T+1, WB is T+2), +counting the first cycle after reset release as cycle 1; instruction 0 is in EX in cycle 3. + + * in order, one per cycle: T(i) >= T(i-1) + 1 + * after a taken branch/jump (flush): T(i) >= T(branch) + 3 (the 2-cycle penalty) + * RAW hazard on source register s whose latest writer is p, with forwarding ON: + loads and CSR reads (result only exists in WB): T(i) >= T(p) + 2 (the load-use stall) + everything else (forwarded from EX/MEM): T(i) >= T(p) + 1 (no stall) + * RAW hazard with forwarding OFF: T(i) >= T(p) + 3 - the consumer waits in ID until the producer + is in WB, where the register file's write-first bypass delivers the value. + +The halting instruction retires after WB, so the run takes T(last) + 2 cycles. +""" + +from __future__ import annotations + +from golden.decode import LOADS, decode +from golden.progbuild import reads + +EX_OF_FIRST = 3 # IF in cycle 1, ID in cycle 2, EX in cycle 3 +FLUSH_GAP = 3 # a redirecting instruction in EX at T lets its target reach EX at T + 3 +WB_AFTER_EX = 2 + + +def is_redirect(r) -> bool: + """Taken branch or any jump: the pipeline flushes IF/ID for each, even a taken branch to pc+4.""" + return r.taken + + +def is_late(insn: int) -> bool: + """Result is only available in WB: loads (data arrives in MEM, not forwarded) and CSR reads.""" + x = decode(insn) + return x.name in LOADS or x.name == "csrrs" + + +def ex_cycles(trace, forward: bool = True) -> list[int]: + """T(i) for every instruction in the trace.""" + T: list[int] = [] + last_writer: dict[int, int] = {} + for i, r in enumerate(trace): + t = EX_OF_FIRST if i == 0 else T[i - 1] + 1 + if i and is_redirect(trace[i - 1]): + t = max(t, T[i - 1] + FLUSH_GAP) + for s in reads(r.insn): + p = last_writer.get(s) + if p is None: + continue + if not forward: + need = T[p] + 3 + else: + need = T[p] + 2 if is_late(trace[p].insn) else T[p] + 1 + t = max(t, need) + T.append(t) + if r.rd_addr: + last_writer[r.rd_addr] = i + return T + + +def pipe_cycles(trace, forward: bool = True) -> int: + """Cycles from reset release until the last instruction's retire is visible.""" + return ex_cycles(trace, forward)[-1] + WB_AFTER_EX if trace else 0 + + +def stalls(trace, forward: bool = True) -> int: + """Cycles lost to data-hazard stalls (excludes the flush penalty and pipeline fill).""" + T = ex_cycles(trace, forward) + lost = 0 + for i in range(1, len(T)): + floor = T[i - 1] + (FLUSH_GAP if is_redirect(trace[i - 1]) else 1) + lost += T[i] - floor + return lost diff --git a/sim/golden/progbuild.py b/sim/golden/progbuild.py new file mode 100644 index 0000000..60cf7f1 --- /dev/null +++ b/sim/golden/progbuild.py @@ -0,0 +1,206 @@ +"""A tiny programmatic assembler for building test programs without a RISC-V toolchain. + + p = Prog(pad=2) # pad: NOPs inserted after every instruction + p.li(1, 0x12345678) + p.label("loop"); p.addi(1, 1, -1); p.bne(1, 0, "loop") + p.exit() + imem, dmem = p.build() # raw little-endian images, as sw/Makefile produces + +With pad=2 any producer is >= 3 instructions ahead of its consumer, the distance the Phase 3 +pipeline (no forwarding, write-first regfile) needs. pad=0 gives dense code for hand-scheduled +sequences (use p.nop(n) between dependent instructions) and for the Phase 4 hazard tests. + +`raw_hazards()` checks a *dynamic* retire trace, so it holds for taken branches and loops too. +""" + +from __future__ import annotations + +from golden.decode import BY_NAME, M32, decode, encode +from golden.iss import CONSOLE_ADDR, DMEM_BASE, IMEM_SIZE, TOHOST_ADDR + +NOP = encode("addi", 0, 0, imm=0) + + +def _lo12(value: int) -> int: + """Signed low 12 bits, so that lui(value - lo) + addi(lo) reconstructs value.""" + return ((value & 0xFFF) ^ 0x800) - 0x800 + + +class Prog: + def __init__(self, pad: int = 2): + self.pad = pad + self._items: list[tuple] = [] + self._data = bytearray() + + # ------------------------------------------------------------ emission + def _emit(self, item: tuple) -> "Prog": + self._items.append(item) + for _ in range(self.pad): + self._items.append(("raw", NOP)) + return self + + def label(self, name: str) -> "Prog": + self._items.append(("label", name)) + return self + + def nop(self, n: int = 1) -> "Prog": + """Explicit NOPs, never padded further.""" + self._items.extend([("raw", NOP)] * n) + return self + + def raw(self, word: int) -> "Prog": + return self._emit(("raw", word & M32)) + + def op(self, name: str, *args) -> "Prog": + """Emit by mnemonic. Operand order follows assembly: + R (rd, rs1, rs2) I/SH/load/jalr (rd, rs1, imm) S (rs2, rs1, imm) B (rs1, rs2, label) + U (rd, imm20) J (rd, label) CSR (rd, csr) SYS/FENCE ()""" + fmt = BY_NAME[name].fmt + if fmt == "R": + rd, rs1, rs2 = args + kw = dict(rd=rd, rs1=rs1, rs2=rs2) + elif fmt in ("I", "SH"): + rd, rs1, imm = args + kw = dict(rd=rd, rs1=rs1, imm=imm) + elif fmt == "S": + rs2, rs1, imm = args + kw = dict(rs2=rs2, rs1=rs1, imm=imm) + elif fmt == "B": + rs1, rs2, target = args + return self._emit(("branch", name, dict(rs1=rs1, rs2=rs2), target)) + elif fmt == "U": + rd, imm20 = args + kw = dict(rd=rd, imm=(imm20 << 12) & M32) + elif fmt == "J": + rd, target = args + return self._emit(("branch", name, dict(rd=rd), target)) + elif fmt == "CSR": + rd, csr = args + kw = dict(rd=rd, rs1=0, csr=csr) + else: + kw = {} + return self._emit(("insn", name, kw)) + + def __getattr__(self, name: str): + if name.startswith("_") or name not in BY_NAME: + raise AttributeError(name) + return lambda *args: self.op(name, *args) + + def _gap(self) -> "Prog": + """NOPs that make a dependent pair of emitted instructions >= 3 apart at any pad.""" + return self.nop(max(0, 2 - self.pad)) + + def li(self, rd: int, value: int) -> "Prog": + value &= M32 + lo = _lo12(value) + self.op("lui", rd, ((value - lo) & M32) >> 12) + self._gap() + return self.op("addi", rd, rd, lo) + + def li_label(self, rd: int, label: str, add: int = 0) -> "Prog": + """rd = address of `label` + add (two instructions, resolved at build time).""" + self._emit(("lui_label", rd, label, add)) + self._gap() + return self._emit(("addi_label", rd, label, add)) + + def exit(self, code: int = 1) -> "Prog": + """Store `code` to TOHOST (1 = PASS). Uses x30/x31.""" + self.li(31, TOHOST_ADDR) + self.li(30, code) + self._gap() + return self.sw(30, 31, 0) + + def putc(self, ch: str | int, scratch: int = 29) -> "Prog": + """Print one character through CONSOLE. Uses x28/x29 by default.""" + self.li(28, CONSOLE_ADDR) + self.li(scratch, ord(ch) if isinstance(ch, str) else ch) + self._gap() + return self.sb(scratch, 28, 0) + + # ------------------------------------------------------------ data (DMEM image) + def align(self, n: int = 4) -> None: + while len(self._data) % n: + self._data.append(0) + + def word(self, *values: int) -> int: + """Append 32-bit words; returns the DMEM address of the first one.""" + self.align(4) + addr = DMEM_BASE + len(self._data) + for v in values: + self._data += (v & M32).to_bytes(4, "little") + return addr + + def bytes_(self, data: bytes) -> int: + addr = DMEM_BASE + len(self._data) + self._data += data + return addr + + def space(self, n: int) -> int: + self.align(4) + addr = DMEM_BASE + len(self._data) + self._data += bytes(n) + return addr + + # ------------------------------------------------------------ build + def build(self) -> tuple[bytes, bytes]: + labels: dict[str, int] = {} + placed: list[tuple[int, tuple]] = [] + addr = 0 + for item in self._items: + if item[0] == "label": + if item[1] in labels: + raise ValueError(f"duplicate label {item[1]}") + labels[item[1]] = addr + else: + placed.append((addr, item)) + addr += 4 + if addr > IMEM_SIZE: + raise ValueError(f"program is {addr} bytes, IMEM holds {IMEM_SIZE}") + + words = [] + for pc, item in placed: + kind = item[0] + if kind == "raw": + words.append(item[1]) + elif kind == "insn": + words.append(encode(item[1], **item[2])) + elif kind == "branch": + _, name, kw, target = item + words.append(encode(name, imm=labels[target] - pc, **kw)) + else: # lui_label / addi_label + _, rd, label, add = item + value = (labels[label] + add) & M32 + lo = _lo12(value) + if kind == "lui_label": + words.append(encode("lui", rd, imm=(value - lo) & M32)) + else: + words.append(encode("addi", rd, rd, imm=lo)) + return b"".join(w.to_bytes(4, "little") for w in words), bytes(self._data) + + +def reads(insn: int) -> tuple[int, ...]: + """Source registers an instruction actually reads (x0 excluded).""" + x = decode(insn) + if x is None: + return () + if x.fmt in ("R", "S", "B"): + regs = (x.rs1, x.rs2) + elif x.fmt in ("I", "SH"): + regs = (x.rs1,) + else: + regs = () + return tuple(r for r in regs if r) + + +def raw_hazards(trace, min_distance: int = 3) -> list[tuple[int, int, int]]: + """Read-after-write pairs in a dynamic retire trace closer than `min_distance` instructions. + Returns (producer index, consumer index, register).""" + last_write: dict[int, int] = {} + bad = [] + for i, r in enumerate(trace): + for reg in reads(r.insn): + if reg in last_write and i - last_write[reg] < min_distance: + bad.append((last_write[reg], i, reg)) + if r.rd_addr: + last_write[r.rd_addr] = i + return bad diff --git a/sim/golden/test_consts.py b/sim/golden/test_consts.py new file mode 100644 index 0000000..4f7664b --- /dev/null +++ b/sim/golden/test_consts.py @@ -0,0 +1,32 @@ +"""sim/consts.py must agree with rtl/tiny_cpu_pkg.sv and golden/iss.py.""" + +import re +from pathlib import Path + +import consts +from golden import iss + +PKG = (Path(__file__).resolve().parents[2] / "rtl" / "tiny_cpu_pkg.sv").read_text() + + +def enum_values(prefix: str) -> dict[str, int]: + return {m.group(1): int(m.group(2)) for m in re.finditer(rf"\b{prefix}_(\w+)\s*=\s*\d+'d(\d+)", PKG)} + + +def test_enum_encodings_match_package(): + assert enum_values("ALU") == consts.ALU + assert enum_values("IMM") == consts.IMM + assert enum_values("WB") == consts.WB + assert enum_values("SZ") == consts.SZ + + +def test_memory_map_matches_package_and_iss(): + def pkg(name): + return int(re.search(rf"{name}\s*=\s*32'h([0-9A-Fa-f_]+)", PKG).group(1).replace("_", ""), 16) + + assert pkg("DMEM_BASE") == consts.DMEM_BASE == iss.DMEM_BASE + assert pkg("CONSOLE_ADDR") == consts.CONSOLE_ADDR == iss.CONSOLE_ADDR + assert pkg("TOHOST_ADDR") == consts.TOHOST_ADDR == iss.TOHOST_ADDR + assert pkg("CYCLE_ADDR") == consts.CYCLE_ADDR == iss.CYCLE_ADDR + assert int(re.search(r"IMEM_WORDS\s*=\s*(\d+)", PKG).group(1)) * 4 == iss.IMEM_SIZE + assert int(re.search(r"DMEM_WORDS\s*=\s*(\d+)", PKG).group(1)) * 4 == iss.DMEM_SIZE diff --git a/sim/golden/test_decode.py b/sim/golden/test_decode.py new file mode 100644 index 0000000..5e4513d --- /dev/null +++ b/sim/golden/test_decode.py @@ -0,0 +1,125 @@ +import random + +import pytest + +from golden.decode import ( + BY_NAME, + INSNS, + REG_NAMES, + decode, + disasm, + encode, + imm_b, + imm_i, + imm_j, + imm_s, + imm_u, + lookup, +) + + +def rand_fields(rng, d): + a = dict(rd=rng.randrange(32), rs1=rng.randrange(32), rs2=rng.randrange(32)) + imm = { + "I": lambda: rng.randint(-2048, 2047), + "SH": lambda: rng.randrange(32), + "S": lambda: rng.randint(-2048, 2047), + "B": lambda: rng.randrange(-2048, 2048) * 2, + "U": lambda: rng.randrange(1 << 20) << 12, + "J": lambda: rng.randrange(-(1 << 19), 1 << 19) * 2, + }.get(d.fmt, lambda: 0)() + return a, imm + + +def test_table_has_every_rv32i_instruction(): + names = {d.name for d in INSNS} + # 38 RV32I base (37 + fence) + ecall/ebreak + the Zicntr csrrs read + assert len(INSNS) == 41 and len(names) == 41 + assert {"lui", "auipc", "jal", "jalr", "sltiu", "srai", "sra", "fence", "ecall"} <= names + + +def test_no_two_definitions_overlap(): + for i, a in enumerate(INSNS): + for b in INSNS[i + 1 :]: + common = a.mask & b.mask + assert a.match & common != b.match & common, f"{a.name} overlaps {b.name}" + + +@pytest.mark.parametrize("d", INSNS, ids=lambda d: d.name) +def test_encode_decode_roundtrip(d): + rng = random.Random(42) + for _ in range(300): + a, imm = rand_fields(rng, d) + w = encode(d.name, imm=imm, csr=0xC00, **a) + x = decode(w) + assert x is not None and x.name == d.name and x.insn == w + assert lookup(w) is BY_NAME[d.name] + if d.fmt in ("I", "SH", "S", "B", "U", "J"): + assert x.imm == (imm if d.fmt != "U" else (imm if imm < 1 << 31 else imm - (1 << 32))) + if d.fmt in ("R", "I", "SH", "U", "J", "CSR"): + assert x.rd == a["rd"] + if d.fmt in ("R", "I", "SH", "S", "B", "CSR"): + assert x.rs1 == a["rs1"] + if d.fmt in ("R", "S", "B"): + assert x.rs2 == a["rs2"] + + +def test_immediate_extremes(): + assert imm_i(encode("addi", imm=-2048)) == -2048 + assert imm_i(encode("addi", imm=2047)) == 2047 + assert imm_s(encode("sw", imm=-1)) == -1 + assert imm_b(encode("beq", imm=-4096)) == -4096 and imm_b(encode("beq", imm=4094)) == 4094 + assert imm_j(encode("jal", imm=-(1 << 20))) == -(1 << 20) + assert imm_j(encode("jal", imm=(1 << 20) - 2)) == (1 << 20) - 2 + assert imm_u(encode("lui", imm=0xFFFFF000)) == -4096 + + +@pytest.mark.parametrize( + "name,kw", + [ + ("addi", dict(imm=2048)), + ("addi", dict(imm=-2049)), + ("beq", dict(imm=3)), # odd offset + ("jal", dict(imm=1 << 20)), + ("slli", dict(imm=32)), + ("add", dict(rd=32)), + ], +) +def test_encode_rejects_bad_operands(name, kw): + with pytest.raises(ValueError): + encode(name, **kw) + + +def test_known_encodings(): + # Reference words produced by GNU as (riscv-none-elf-gcc 15.2). + assert encode("addi", 1, 0, imm=5) == 0x00500093 + assert encode("add", 3, 1, 2) == 0x002081B3 + assert encode("sub", 3, 1, 2) == 0x402081B3 + assert encode("lw", 5, 2, imm=8) == 0x00812283 + assert encode("sw", 0, 2, 5, imm=8) == 0x00512423 + assert encode("lui", 1, imm=0x12345000) == 0x123450B7 + assert encode("jal", 0, imm=0) == 0x0000006F + assert encode("ecall") == 0x00000073 and encode("ebreak") == 0x00100073 + + +def test_illegal_words_do_not_decode(): + for w in (0x00000000, 0xFFFFFFFF, 0x0000007F, 0x40001033, 0x00100073 | 1 << 7): + assert decode(w) is None, hex(w) + + +def test_disassembly(): + assert disasm(0x00500093) == "addi ra, zero, 5" + assert disasm(encode("lw", 10, 2, imm=-4)) == "lw a0, -4(sp)" + assert disasm(encode("sw", 0, 2, 10, imm=12)) == "sw a0, 12(sp)" + assert disasm(encode("beq", 0, 1, 2, imm=-8), pc=0x20) == "beq ra, sp, 0x18" + assert disasm(encode("lui", 3, imm=0x12345000)) == "lui gp, 0x12345" + assert disasm(encode("slli", 1, 2, imm=3)) == "slli ra, sp, 3" + assert disasm(0) == ".word 0x00000000" + assert disasm(encode("csrrs", 5, 0, csr=0xC00)) == "csrrs t0, cycle, zero" + assert len(REG_NAMES) == 32 + + +def test_generated_isa_ts_is_up_to_date(): + from golden.gen_isa_ts import OUT, render + + assert OUT.exists() and OUT.read_text() == render(), "run: cd sim && python -m golden.gen_isa_ts" diff --git a/sim/golden/test_gnu_asm_fixtures.py b/sim/golden/test_gnu_asm_fixtures.py new file mode 100644 index 0000000..a735c69 --- /dev/null +++ b/sim/golden/test_gnu_asm_fixtures.py @@ -0,0 +1,17 @@ +"""web/src/lib/__tests__/gnu-asm.json is current. Needs the RISC-V GNU toolchain (riscv-none-elf-gcc).""" + +import os +import shutil + +import pytest + +from golden.gen_asm_fixtures import main + + +def test_gnu_asm_fixtures_are_current(): + if shutil.which("riscv-none-elf-gcc") is None: + msg = "riscv-none-elf-gcc not on PATH (bash .tools/riscv-gcc.sh && source scripts/env.sh)" + if os.environ.get("REQUIRE_RISCV_TESTS") == "1": + pytest.fail(msg) + pytest.skip(msg) + assert main(["--check"]) == 0, "run: cd sim && python -m golden.gen_asm_fixtures" diff --git a/sim/golden/test_iss.py b/sim/golden/test_iss.py new file mode 100644 index 0000000..92970d0 --- /dev/null +++ b/sim/golden/test_iss.py @@ -0,0 +1,305 @@ +"""ISS unit tests: instruction semantics, memory lanes, MMIO, faults and retire records.""" + +import pytest + +from golden.decode import encode as e +from golden.iss import ( + CONSOLE_ADDR, + CYCLE_ADDR, + DMEM_BASE, + TOHOST_ADDR, + BusFault, + IllegalInstruction, + Iss, + MisalignedFetch, +) + +M32 = 0xFFFFFFFF + + +def words(*ws): + return b"".join((w & M32).to_bytes(4, "little") for w in ws) + + +def li(rd, value): + """lui+addi pair loading an arbitrary 32-bit constant.""" + value &= M32 + lo = ((value & 0xFFF) ^ 0x800) - 0x800 + return [e("lui", rd, imm=(value - lo) & M32), e("addi", rd, rd, imm=lo)] + + +EXIT = [e("lui", 31, imm=TOHOST_ADDR & ~0xFFF), e("addi", 30, 0, imm=1), e("sw", 0, 31, 30, imm=4)] + + +def run(*prog, dmem=b"", expect_pass=True): + iss = Iss(words(*prog, *EXIT), dmem) + trace = iss.run(10_000) + assert iss.passed is expect_pass + return iss, trace + + +def reg(iss, n): + return iss.regs[n] + + +# ------------------------------------------------------------------ ALU + + +@pytest.mark.parametrize( + "op,a,b,want", + [ + ("add", 0xFFFFFFFF, 1, 0), + ("add", 0x7FFFFFFF, 1, 0x80000000), + ("sub", 0, 1, 0xFFFFFFFF), + ("sub", 0x80000000, 1, 0x7FFFFFFF), + ("sll", 1, 31, 0x80000000), + ("sll", 1, 32, 1), # shift amount is masked to 5 bits + ("srl", 0x80000000, 31, 1), + ("sra", 0x80000000, 31, 0xFFFFFFFF), + ("sra", 0x7FFFFFFF, 31, 0), + ("slt", 0xFFFFFFFF, 0, 1), # -1 < 0 signed + ("sltu", 0xFFFFFFFF, 0, 0), + ("slt", 0x80000000, 0x7FFFFFFF, 1), + ("sltu", 0x80000000, 0x7FFFFFFF, 0), + ("xor", 0xF0F0F0F0, 0xFFFF0000, 0x0F0FF0F0), + ("or", 0xF0F0F0F0, 0x0F0F0F0F, 0xFFFFFFFF), + ("and", 0xF0F0F0F0, 0xFFFF0000, 0xF0F00000), + ], +) +def test_register_alu(op, a, b, want): + iss, _ = run(*li(1, a), *li(2, b), e(op, 3, 1, 2)) + assert reg(iss, 3) == want + + +@pytest.mark.parametrize( + "op,a,imm,want", + [ + ("addi", 5, -6, M32), + ("slti", 0xFFFFFFFE, -1, 1), + ("sltiu", 0, -1, 1), # immediate sign-extends, then compares unsigned + ("sltiu", 0xFFFFFFFF, -1, 0), + ("xori", 0xFF, -1, 0xFFFFFF00), + ("ori", 0x0F, -16, 0xFFFFFFFF), + ("andi", 0xFFFFFFFF, -1, 0xFFFFFFFF), + ("slli", 3, 4, 48), + ("srli", 0x80000000, 4, 0x08000000), + ("srai", 0x80000000, 4, 0xF8000000), + ], +) +def test_immediate_alu(op, a, imm, want): + iss, _ = run(*li(1, a), e(op, 3, 1, imm=imm)) + assert reg(iss, 3) == want + + +def test_x0_is_hardwired_zero_and_discards_writes(): + iss, trace = run(e("addi", 0, 0, imm=123), e("add", 1, 0, 0), e("lui", 0, imm=0x12345000)) + assert reg(iss, 0) == 0 and reg(iss, 1) == 0 + # writes to x0 retire with no destination; the real write to x1 (add x1,x0,x0) does + assert [(r.rd_addr, r.rd_wdata) for r in trace[:3]] == [(0, 0), (1, 0), (0, 0)] + + +def test_lui_auipc(): + iss, _ = run(e("lui", 1, imm=0x80000000), e("auipc", 2, imm=0x1000)) + assert reg(iss, 1) == 0x80000000 + assert reg(iss, 2) == 4 + 0x1000 + + +# ------------------------------------------------------------------ control flow + + +def test_jal_and_jalr_link_and_target(): + iss, trace = run( + e("jal", 1, imm=8), # 0: x1=4, jump to 8 + e("addi", 5, 0, imm=99), # 4: skipped + e("addi", 6, 0, imm=17), # 8 + *li(7, 0x19), # 0xc,0x10: x7 = 0x19 (bit0 set) + e("jalr", 2, 7, imm=0), # 0x14: target 0x19 & ~1 = 0x18, x2 = 0x18 + e("addi", 5, 0, imm=77), # 0x18: executed + ) + assert (reg(iss, 1), reg(iss, 5), reg(iss, 6), reg(iss, 2)) == (4, 77, 17, 0x18) + assert trace[0].pc_wdata == 8 + + +@pytest.mark.parametrize( + "op,a,b,taken", + [ + ("beq", 5, 5, True), ("beq", 5, 6, False), + ("bne", 5, 6, True), ("bne", 5, 5, False), + ("blt", -1, 0, True), ("blt", 0, -1, False), ("blt", 3, 3, False), + ("bge", 0, -1, True), ("bge", -1, 0, False), ("bge", 3, 3, True), + ("bltu", 0, -1, True), ("bltu", -1, 0, False), + ("bgeu", -1, 0, True), ("bgeu", 0, -1, False), ("bgeu", 7, 7, True), + ], +) +def test_branches(op, a, b, taken): + iss, trace = run(*li(1, a), *li(2, b), e(op, 0, 1, 2, imm=8), e("addi", 3, 0, imm=1)) + br = trace[4] # two li pairs occupy 0x0-0xc, so the branch is at 0x10 + assert br.pc == 0x10 and br.pc_wdata == (0x18 if taken else 0x14) + assert reg(iss, 3) == (0 if taken else 1) + + +def test_backward_branch_loop(): + iss, _ = run(e("addi", 1, 0, imm=5), e("addi", 2, 2, imm=3), e("addi", 1, 1, imm=-1), e("bne", 0, 1, 0, imm=-8)) + assert reg(iss, 2) == 15 + + +# ------------------------------------------------------------------ memory + + +def test_load_store_all_sizes_signedness_and_lanes(): + base = DMEM_BASE + iss, trace = run( + *li(1, base), + *li(2, 0x8182_8384), + e("sw", 0, 1, 2, imm=0), + e("lb", 3, 1, imm=0), # 0x84 -> sign-extend + e("lbu", 4, 1, imm=0), + e("lb", 5, 1, imm=3), # 0x81 + e("lh", 6, 1, imm=2), # 0x8182 + e("lhu", 7, 1, imm=2), + e("lh", 8, 1, imm=0), # 0x8384 + e("lw", 9, 1, imm=0), + ) + assert reg(iss, 3) == 0xFFFFFF84 and reg(iss, 4) == 0x84 + assert reg(iss, 5) == 0xFFFFFF81 + assert reg(iss, 6) == 0xFFFF8182 and reg(iss, 7) == 0x8182 + assert reg(iss, 8) == 0xFFFF8384 and reg(iss, 9) == 0x81828384 + by_pc = {r.pc: r for r in trace} + lb3 = next(r for r in trace if r.rd_addr == 5) + assert (lb3.mem_addr, lb3.mem_rmask, lb3.mem_rdata) == (base, 0b1000, 0x81000000) + lh2 = next(r for r in trace if r.rd_addr == 6) + assert (lh2.mem_rmask, lh2.mem_rdata) == (0b1100, 0x81820000) + assert by_pc # trace is keyed by pc without collisions in straight-line code + + +def test_store_byte_and_half_only_touch_their_lanes(): + base = DMEM_BASE + iss, trace = run( + *li(1, base), + *li(2, 0x1122_3344), + e("sw", 0, 1, 2, imm=0), + *li(3, 0xAABBCCDD), + e("sb", 0, 1, 3, imm=1), + e("sh", 0, 1, 3, imm=2), + e("lw", 4, 1, imm=0), + ) + assert reg(iss, 4) == 0xCCDD_DD44 + sb = next(r for r in trace if r.mem_wmask == 0b0010) + assert (sb.mem_addr, sb.mem_wdata) == (base, 0x0000_DD00) + sh = next(r for r in trace if r.mem_wmask == 0b1100) + assert sh.mem_wdata == 0xCCDD_0000 + + +def test_misaligned_access_is_forced_into_alignment_and_flagged(): + base = DMEM_BASE + iss, trace = run( + *li(1, base), + *li(2, 0x4433_2211), + e("sw", 0, 1, 2, imm=0), + e("lw", 3, 1, imm=1), # forced to offset 0 + e("lh", 4, 1, imm=3), # forced to offset 2 + e("lw", 5, 1, imm=0), # aligned control + ) + assert reg(iss, 3) == 0x44332211 and reg(iss, 4) == 0x4433 + flags = [r.misaligned for r in trace if r.mem_rmask] + assert flags == [True, True, False] + + +def test_initial_dmem_image_and_dmem_is_not_instruction_memory(): + iss, _ = run(*li(1, DMEM_BASE), e("lw", 2, 1, imm=4), dmem=bytes(range(8))) + assert reg(iss, 2) == 0x07060504 + + +# ------------------------------------------------------------------ MMIO, halt, csr + + +def test_console_collects_lane0_byte(): + seen = [] + iss = Iss( + words(*li(1, CONSOLE_ADDR), e("addi", 2, 0, imm=ord("H")), e("sb", 0, 1, 2), e("addi", 2, 0, imm=ord("i")), + e("sw", 0, 1, 2, imm=0), *EXIT), + on_console=seen.append, + ) + iss.run() + assert iss.console_text == "Hi" and seen == [72, 105] + + +def test_tohost_codes(): + iss = Iss(words(*li(1, TOHOST_ADDR), e("addi", 2, 0, imm=7), e("sw", 0, 1, 2))) + trace = iss.run() + assert iss.tohost == 7 and not iss.passed + assert trace[-1].halt and trace[-1].halt_reason == "tohost" and trace[-1].mem_wdata == 7 + + +@pytest.mark.parametrize("insn,reason", [(e("ecall"), "ecall"), (e("ebreak"), "ebreak")]) +def test_ecall_ebreak_halt_after_retiring(insn, reason): + iss = Iss(words(e("addi", 1, 0, imm=1), insn, e("addi", 1, 0, imm=2))) + trace = iss.run() + assert len(trace) == 2 and trace[-1].halt and trace[-1].halt_reason == reason + assert reg(iss, 1) == 1 and iss.halted and iss.tohost is None + with pytest.raises(RuntimeError): + iss.step() + + +def test_fence_is_a_nop(): + iss, trace = run(e("fence"), e("addi", 1, 0, imm=3)) + assert reg(iss, 1) == 3 and trace[0].rd_addr == 0 + + +def test_cycle_counters_are_flagged_cycle_dependent(): + iss, trace = run( + e("addi", 1, 0, imm=1), e("csrrs", 2, 0, csr=0xC00), e("csrrs", 3, 0, csr=0xC02), e("csrrs", 4, 0, csr=0xC80), + *li(5, CYCLE_ADDR), e("lw", 6, 5, imm=0), + ) + assert reg(iss, 3) == 2 and reg(iss, 4) == 0 + assert trace[1].cycle_dependent and not trace[2].cycle_dependent + assert next(r for r in trace if r.rd_addr == 6).cycle_dependent + + +# ------------------------------------------------------------------ faults + + +@pytest.mark.parametrize( + "prog,exc", + [ + ([0x00000000], IllegalInstruction), + ([e("csrrs", 1, 1, csr=0xC00)], IllegalInstruction), # rs1 != x0 would write the csr + ([e("csrrs", 1, 0, csr=0x300)], IllegalInstruction), # unknown csr + ([e("jal", 0, imm=6)], MisalignedFetch), + ([e("beq", 0, 0, 0, imm=2)], MisalignedFetch), + ([e("jal", 0, imm=-4)], BusFault), # fetch below IMEM -> wraps outside + ([e("lw", 1, 0, imm=0)], BusFault), # IMEM is not readable by the data port + ([e("sw", 0, 0, 0, imm=0)], BusFault), # ... nor writable + ([e("lui", 1, imm=TOHOST_ADDR & ~0xFFF), e("sb", 0, 1, 0, imm=4)], BusFault), # TOHOST needs sw + ([e("lui", 1, imm=CYCLE_ADDR & ~0xFFF), e("sw", 0, 1, 0, imm=8)], BusFault), # CYCLE is read-only + ], +) +def test_faults(prog, exc): + with pytest.raises(exc): + Iss(words(*prog)).run(100) + + +def test_runaway_program_times_out(): + with pytest.raises(TimeoutError): + Iss(words(e("jal", 0, imm=0))).run(50) + + +def test_oversized_image_rejected(): + with pytest.raises(ValueError): + Iss(bytes(4097)) + + +def test_retire_order_and_pc_chain(): + _, trace = run(e("addi", 1, 0, imm=1), e("jal", 0, imm=8), e("addi", 1, 0, imm=9), e("addi", 2, 0, imm=2)) + assert [r.order for r in trace] == list(range(len(trace))) + for a, b in zip(trace, trace[1:]): + assert a.pc_wdata == b.pc + + +def test_taken_flag_marks_every_redirect_including_a_branch_to_the_next_instruction(): + _, trace = run(e("beq", 0, 0, 0, imm=4), # taken, but the target is pc+4 + e("bne", 0, 0, 0, imm=8), # not taken + e("jal", 0, imm=4), e("jalr", 0, 0, imm=0x10), e("addi", 1, 0, imm=1)) + assert [r.taken for r in trace[:4]] == [True, False, True, True] + assert not any(r.taken for r in trace[4:]) + assert trace[0].pc_wdata == trace[0].pc + 4 # which is exactly why pc_wdata alone is not enough diff --git a/sim/golden/test_perf_baseline.py b/sim/golden/test_perf_baseline.py new file mode 100644 index 0000000..f2707e1 --- /dev/null +++ b/sim/golden/test_perf_baseline.py @@ -0,0 +1,32 @@ +"""The pinned cycle counts must not drift unnoticed (change them deliberately: python -m golden.perf --update).""" + +import json +import os + +import pytest + +from golden.perf import BASELINE, SW_PROGRAMS, measure_all + + +def test_baseline_matches_measurement(): + pinned = json.loads(BASELINE.read_text()) + got = measure_all() + if not all(name in got for name in SW_PROGRAMS): + if os.environ.get("REQUIRE_RISCV_TESTS") == "1": + pytest.fail("sw programs not built (make -C sw programs)") + pytest.skip("sw programs not built; only checking hazard_free entries") + assert got == pinned + + +def test_baseline_entries_that_do_not_need_a_toolchain(): + pinned = json.loads(BASELINE.read_text()) + for name, d in measure_all().items(): + if name.startswith("hazard_free/"): + assert pinned[name] == d, name + + +def test_forwarding_never_hurts_and_pipeline_beats_nothing_it_cannot(): + for name, d in json.loads(BASELINE.read_text()).items(): + assert d["pipe_fwd"] <= d["pipe_nofwd"], name # forwarding only removes stalls + assert d["pipe_fwd"] >= d["instret"] + 4, name # never faster than one per cycle + fill + assert d["single"] == d["instret"], name # single-cycle CPI is exactly 1 diff --git a/sim/golden/test_pipemodel.py b/sim/golden/test_pipemodel.py new file mode 100644 index 0000000..bd30d7f --- /dev/null +++ b/sim/golden/test_pipemodel.py @@ -0,0 +1,72 @@ +"""The timing model, checked by hand-derived cases (no RTL) and against the hazard-free baseline.""" + +import pytest + +from golden.decode import encode as e +from golden.hazard_cases import ADD, CASES, DMEM, NOP, SETUP # noqa: F401 +from golden.hazard_free import PROGRAMS, random_program +from golden.hazard_random import random_hazard_program +from golden.iss import Iss +from golden.pipemodel import ex_cycles, is_late, pipe_cycles, stalls +from golden.progbuild import Prog, raw_hazards + + +def trace_of(*insns): + iss = Iss(b"".join(w.to_bytes(4, "little") for w in (*insns, e("ecall"))), DMEM) + return iss.run(1000) + + +def extra(trace, forward): + """Cycles beyond the ideal N + 4.""" + return pipe_cycles(trace, forward) - (len(trace) + 4) + + +@pytest.mark.parametrize("name", CASES) +def test_hand_derived_cycle_counts(name): + insns, with_fwd, without = CASES[name] + t = trace_of(*insns) + assert extra(t, True) == with_fwd, "forwarding on" + assert extra(t, False) == without, "forwarding off" + + +def test_taken_branch_is_a_flush_not_a_stall(): + t = trace_of(e("jal", 0, imm=8), e("addi", 7, 0, imm=1), e("addi", 8, 0, imm=2)) + assert stalls(t, True) == 0 and stalls(t, False) == 0 and extra(t, True) == 2 + + +def test_late_results_are_loads_and_csr_reads_only(): + assert is_late(e("lw", 1, 2, imm=0)) and is_late(e("lbu", 1, 2, imm=0)) and is_late(e("csrrs", 1, 0, csr=0xC00)) + assert not is_late(e("addi", 1, 2, imm=0)) and not is_late(e("jal", 1, imm=4)) and not is_late(e("sw", 0, 1, 2)) + + +def test_first_instruction_is_in_ex_on_cycle_3(): + assert ex_cycles(trace_of(e("addi", 1, 0, imm=1)))[0] == 3 + + +@pytest.mark.parametrize("name", sorted(PROGRAMS)) +def test_hazard_free_programs_have_no_data_stalls_either_way(name): + iss = Iss(*PROGRAMS[name]().build()) + t = iss.run(100_000) + if name == "mem_raw_adjacent": # hand-scheduled: also free of register hazards + pass + assert raw_hazards(t) == [] and stalls(t, True) == 0 and stalls(t, False) == 0 + + +@pytest.mark.parametrize("seed", range(20)) +def test_hazard_random_programs_terminate_and_have_real_hazards(seed): + iss = Iss(*random_hazard_program(seed).build()) + t = iss.run(100_000) + assert iss.passed + assert stalls(t, False) >= stalls(t, True) >= 0 + assert len(t) > 40 + + +def test_hazard_random_programs_actually_stress_the_pipeline(): + total_fwd = total_nofwd = hazards = 0 + for seed in range(40): + t = Iss(*random_hazard_program(seed).build()) + tr = t.run(100_000) + total_fwd += stalls(tr, True) + total_nofwd += stalls(tr, False) + hazards += len(raw_hazards(tr)) + assert hazards > 500 and total_fwd > 40 and total_nofwd > 5 * total_fwd diff --git a/sim/golden/test_progbuild.py b/sim/golden/test_progbuild.py new file mode 100644 index 0000000..4b8f261 --- /dev/null +++ b/sim/golden/test_progbuild.py @@ -0,0 +1,119 @@ +"""Program builder + hazard-free program set, checked on the ISS alone (no RTL, no toolchain).""" + +import struct + +import pytest + +from golden.decode import encode as e +from golden.hazard_free import PROGRAMS, random_program +from golden.iss import CONSOLE_ADDR, DMEM_BASE, Iss +from golden.progbuild import NOP, Prog, raw_hazards, reads + + +def run(prog): + imem, dmem = prog.build() + iss = Iss(imem, dmem) + trace = iss.run(200_000) + return iss, trace + + +def test_pad_inserts_nops_after_every_instruction_and_labels_resolve(): + p = Prog(pad=2) + p.label("top").addi(1, 0, 1).beq(0, 0, "top") + imem, _ = p.build() + words = [int.from_bytes(imem[i : i + 4], "little") for i in range(0, len(imem), 4)] + assert words[0] == e("addi", 1, 0, imm=1) and words[1:3] == [NOP, NOP] + assert words[3] == e("beq", 0, 0, 0, imm=-12) and words[4:] == [NOP, NOP] + + +def test_li_and_li_label_reconstruct_any_value(): + for v in (0, 1, 0x7FF, 0x800, 0xFFF, 0x1000, 0x80000000, 0x7FFFFFFF, 0xFFFFF800, 0xDEADBEEF, 0xFFFFFFFF): + p = Prog(pad=0) + p.li(5, v) + imem = p.build()[0] + iss = Iss(imem) + while iss.pc < len(imem): # li may include gap NOPs at pad=0 + iss.step() + assert iss.regs[5] == v, hex(v) + p = Prog(pad=0) + p.nop(3).label("here").li_label(6, "here", 1) + imem = p.build()[0] + iss = Iss(imem) + while iss.pc < len(imem): + iss.step() + assert iss.regs[6] == 13 + + +def test_data_section_layout(): + p = Prog() + a = p.word(1, 2) + b = p.bytes_(b"xyz") + c = p.word(7) # re-aligned + assert (a, b, c) == (DMEM_BASE, DMEM_BASE + 8, DMEM_BASE + 12) + assert p.build()[1] == struct.pack("= s32(b), + "bltu": lambda a, b: a < b, "bgeu": lambda a, b: a >= b} + want = 0 + for op in cond: + for a, b in ((1, 3), (1, 2), (2, 1)): + want += 1 + (0 if cond[op](vals[a], vals[b]) else 100) + iss, _ = run(PROGRAMS["branches"]()) + loop_sum, count = struct.unpack(""]) +def test_rv32ui(stem): + if stem == "": + pytest.skip("riscv-tests not built") + iss = Iss.from_build(BUILD / stem) + trace = iss.run(max_steps=100_000) + tail = "\n".join(str(r) for r in trace[-6:]) + assert iss.passed, ( + f"{stem}: tohost={iss.tohost} -> failed test case #{(iss.tohost or 0) >> 1}\nlast retires:\n{tail}" + ) diff --git a/sim/golden/test_vs_gnu.py b/sim/golden/test_vs_gnu.py new file mode 100644 index 0000000..6c5ba45 --- /dev/null +++ b/sim/golden/test_vs_gnu.py @@ -0,0 +1,48 @@ +"""Cross-check decode/encode against real GNU toolchain output (riscv-tests + our programs). + +For every instruction word GCC emitted: our mnemonic must equal objdump's (aliases disabled), +and re-encoding the decoded fields must reproduce the word bit for bit. +""" + +import os +import re +from pathlib import Path + +import pytest + +from golden.decode import decode, encode + +BUILD = Path(__file__).resolve().parents[2] / "sw" / "build" +DIS = sorted(BUILD.glob("*/*.dis")) +LINE = re.compile(r"^\s*([0-9a-f]+):\s+([0-9a-f]{8})\s+(\S+)") +UNIMP = 0xC0001073 # `unimp` = csrrw cycle,x0 - emitted by RVTEST_CODE_END, never executed + + +def test_dis_files_present(): + if not DIS: + msg = "no .dis files (run `make -C sw all`)" + if os.environ.get("REQUIRE_RISCV_TESTS") == "1": + pytest.fail(msg) + pytest.skip(msg) + + +@pytest.mark.parametrize("path", DIS or [Path("")], ids=lambda p: p.stem) +def test_matches_objdump(path): + if not path.exists(): + pytest.skip("not built") + checked = 0 + for line in path.read_text().splitlines(): + m = LINE.match(line) + if not m: + continue + word, mnem = int(m.group(2), 16), m.group(3) + if word == UNIMP: + continue + x = decode(word) + assert x is not None, f"{path.stem}: cannot decode 0x{word:08x} ({mnem})" + assert x.name == mnem, f"{path.stem}: 0x{word:08x} objdump={mnem} ours={x.name}" + if x.name != "fence": # GCC emits `fence rw,rw` etc.; we only model the plain encoding + again = encode(x.name, x.rd, x.rs1, x.rs2, x.imm, x.csr) + assert again == word, f"{path.stem}: {x.name} 0x{word:08x} re-encodes to 0x{again:08x}" + checked += 1 + assert checked > 0 diff --git a/sim/golden/test_wasm_fixtures.py b/sim/golden/test_wasm_fixtures.py new file mode 100644 index 0000000..ec4d7a1 --- /dev/null +++ b/sim/golden/test_wasm_fixtures.py @@ -0,0 +1,52 @@ +"""wasm/test/fixtures.json is current and agrees with the golden ISS's own answers.""" + +from golden.wasm_fixtures import OUT, build_fixture, render, retire_digest # noqa: F401 +from golden.iss import Iss +from golden.hazard_free import PROGRAMS +import json + + +def test_fixtures_are_current(): + from golden.wasm_fixtures import main + + assert main(["--check"]) == 0, "run: cd sim && python -m golden.wasm_fixtures" + + +def test_fixture_answers_are_the_expected_ones(): + data = json.loads(OUT.read_text()) + assert data["hazard_free/fib"]["expect"]["dmemHex"] == (6765).to_bytes(4, "little").hex() + assert data["hazard_free/console_and_csr"]["console"] == "Hi, pipe!\n!" + assert data["hazard_free/bubble_sort"]["instret"] > 0 + + +def test_fixture_cycles_agree_with_the_pinned_baseline(): + import json as _json + + from golden.perf import BASELINE + + pinned = _json.loads(BASELINE.read_text()) + data = _json.loads(OUT.read_text()) + shared = [k for k in data if k in pinned] + assert shared, "no fixture is in the perf baseline" + for name in shared: + assert data[name]["cycles"] == { + "single": pinned[name]["single"], + "pipeFwd": pinned[name]["pipe_fwd"], + "pipeNoFwd": pinned[name]["pipe_nofwd"], + }, name + + +def test_hazard_fixture_shows_the_forwarding_pin(): + data = json.loads(OUT.read_text()) + c = data["hazard_dense/3"]["cycles"] + assert c["pipeFwd"] != c["pipeNoFwd"] + + +def test_digest_depends_on_the_retire_stream(): + imem, dmem = PROGRAMS["fib"]().build() + trace = Iss(imem, dmem).run(200_000) + d = retire_digest(trace) + assert d == retire_digest(Iss(imem, dmem).run(200_000)) # deterministic + tampered = list(trace) + tampered[5] = type(trace[5])(**{**trace[5].__dict__, "rd_wdata": trace[5].rd_wdata ^ 1}) + assert retire_digest(tampered) != d diff --git a/sim/golden/wasm_fixtures.py b/sim/golden/wasm_fixtures.py new file mode 100644 index 0000000..a39a1a6 --- /dev/null +++ b/sim/golden/wasm_fixtures.py @@ -0,0 +1,120 @@ +"""Fixtures for the WASM harness tests (wasm/test.mjs), generated from the golden ISS. + + cd sim && python -m golden.wasm_fixtures # rewrite wasm/test/fixtures.json + cd sim && python -m golden.wasm_fixtures --check # exit 1 if stale (pytest does this too) + +Each fixture carries the ISS's expected console output, instruction count, final registers, memory +expectations and a SHA-256 digest of the retire stream. The Node test compares the WASM build against +all of them, so the browser build is checked against the Python reference, not against itself. + +Cycle counts come from the independent pipeline model (golden/pipemodel.py). They are stored per fixture +and must agree with sim/perf_baseline.json wherever both list the program (test_wasm_fixtures.py checks it). +""" + +from __future__ import annotations + +import hashlib +import json +import struct +import sys +from pathlib import Path + +from golden.hazard_free import PROGRAMS +from golden.hazard_random import random_hazard_program +from golden.iss import DMEM_BASE, Iss +from golden.pipemodel import pipe_cycles + +ROOT = Path(__file__).resolve().parents[2] +OUT = ROOT / "wasm" / "test" / "fixtures.json" +# Hazard-free programs take the same cycles with forwarding on and off (that is their point), so one +# hazard-dense program is included: it is the only fixture on which the forwarding pin is observable. +HAZARD_SEED = 3 +FIXTURE_PROGRAMS = ["fib", "console_and_csr", "bubble_sort", f"hazard_dense/{HAZARD_SEED}"] + +# The retire-record fields hashed into the digest, in order. Cycle-dependent values (rdcycle, +# rdtime, CYCLE MMIO) are replaced by 0 because they are timing, not architecture. +DIGEST_FMT = "<13I" + + +def retire_digest(trace) -> str: + h = hashlib.sha256() + for r in trace: + rd = 0 if r.cycle_dependent else r.rd_wdata + md = 0 if r.cycle_dependent else r.mem_rdata + h.update( + struct.pack( + DIGEST_FMT, + r.order, r.pc, r.insn, r.rd_addr, rd, r.pc_wdata, r.mem_addr, + r.mem_rmask, r.mem_wmask, md, r.mem_wdata, int(r.misaligned), int(r.halt), + ) + ) + return h.hexdigest() + + +def timing_registers(trace) -> list[int]: + """Registers whose final value depends on timing (last written by a cycle-dependent retire).""" + timing: set[int] = set() + for r in trace: + if r.rd_addr: + (timing.add if r.cycle_dependent else timing.discard)(r.rd_addr) + return sorted(timing) + + +def _program(name: str): + if name.startswith("hazard_dense/"): + return random_hazard_program(int(name.split("/")[1])) + return PROGRAMS[name]() + + +def build_fixture(name: str) -> dict: + imem, dmem = _program(name).build() + iss = Iss(imem, dmem) + trace = iss.run(200_000) + assert iss.passed, f"{name}: fixture program does not pass on the ISS" + cycles = { + "single": iss.instret, + "pipeFwd": pipe_cycles(trace, True), + "pipeNoFwd": pipe_cycles(trace, False), + } + if name.startswith("hazard_dense/"): + assert cycles["pipeFwd"] != cycles["pipeNoFwd"], "hazard fixture must show the forwarding pin" + expect = {} + if name == "fib": + expect = {"dmemAddr": DMEM_BASE, "dmemHex": (6765).to_bytes(4, "little").hex()} + elif name == "bubble_sort": + values = sorted([42, 7, 99, 3, 18, 64, 1, 27]) + expect = {"dmemAddr": DMEM_BASE, "dmemHex": struct.pack("<8I", *values).hex()} + return { + "imem": imem.hex(), + "dmem": dmem.hex(), + "console": iss.console_text, + "instret": iss.instret, + "cycles": cycles, + "regs": iss.regs, + "timingRegs": timing_registers(trace), + "digest": retire_digest(trace), + "expect": expect, + } + + +def render() -> str: + data = {(n if n.startswith("hazard_dense/") else f"hazard_free/{n}"): build_fixture(n) + for n in FIXTURE_PROGRAMS} + return json.dumps(data, indent=1, sort_keys=True) + "\n" + + +def main(argv: list[str]) -> int: + text = render() + if "--check" in argv: + if not OUT.exists() or OUT.read_text() != text: + print(f"{OUT} is stale: run `cd sim && python -m golden.wasm_fixtures`", file=sys.stderr) + return 1 + return 0 + OUT.parent.mkdir(parents=True, exist_ok=True) + OUT.write_text(text) + print(f"wrote {OUT}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv[1:])) diff --git a/sim/lockstep_lib.py b/sim/lockstep_lib.py new file mode 100644 index 0000000..a6d15e5 --- /dev/null +++ b/sim/lockstep_lib.py @@ -0,0 +1,176 @@ +"""Shared lockstep machinery: RTL (tiny_cpu_top) vs the golden ISS, one retire at a time. + +The ISS steps once per rvfi_valid pulse and every retire field must match; the first divergence stops +the run with the ISS's last retired instructions. Also checked: faults (ISS raises <=> RTL sets +`fault`, with no retire), a frozen core after halt/fault, MMIO side effects, final registers, and the +cycle count for the core under test (TINYCPU_CORE = SINGLE | PIPE). +""" + +import json +import os +from pathlib import Path + +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import FallingEdge, ReadOnly, RisingEdge, Timer + +from golden.pipemodel import pipe_cycles +from golden.decode import encode as e # noqa: F401 (re-exported for tests) +from golden.iss import IMEM_SIZE, DMEM_SIZE, Iss, IssError + +CORE = os.environ.get("TINYCPU_CORE", "SINGLE") + +ROOT = Path(__file__).resolve().parent.parent +BUILD = ROOT / "sw" / "build" +EXPECTED = json.loads((ROOT / "sim" / "programs" / "expected.json").read_text()) +MAX_CYCLES = 200_000 + +FIELDS = [ # (RTL signal, ISS Retire attribute, cycle-dependent?) + ("rvfi_order", "order", False), ("rvfi_pc", "pc", False), ("rvfi_insn", "insn", False), + ("rvfi_rd_addr", "rd_addr", False), ("rvfi_rd_wdata", "rd_wdata", True), + ("rvfi_pc_wdata", "pc_wdata", False), ("rvfi_mem_addr", "mem_addr", False), + ("rvfi_mem_rmask", "mem_rmask", False), ("rvfi_mem_wmask", "mem_wmask", False), + ("rvfi_mem_rdata", "mem_rdata", True), ("rvfi_mem_wdata", "mem_wdata", False), + ("rvfi_misaligned", "misaligned", False), ("rvfi_halt", "halt", False), +] + + +def images(): + out = [] + for group in ("riscv-tests", "programs"): + out += sorted(p.name.removesuffix(".imem.bin") for p in (BUILD / group).glob("*.imem.bin")) + return out + + +def stem_path(name): + group = "riscv-tests" if name.startswith("rv32ui") else "programs" + return BUILD / group / name + + +def words(data: bytes, count: int) -> list[int]: + data = bytes(data).ljust(count * 4, b"\0") + return [int.from_bytes(data[4 * i : 4 * i + 4], "little") for i in range(count)] + + +def prog_bytes(*ws) -> bytes: + return b"".join((w & 0xFFFFFFFF).to_bytes(4, "little") for w in ws) + + +_clock = None + + +def ensure_clock(dut): + """One clock driver per test (a test may call lockstep() many times; finished tasks are dead).""" + global _clock + if _clock is None or _clock.done(): + _clock = cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start()) + + +async def reset_and_load(dut, imem: bytes, dmem: bytes, forward: int = 1): + ensure_clock(dut) + await Timer(1, "ns") # a previous lockstep() ends in the ReadOnly phase, where writes are illegal + dut.rst_n.value, dut.load_en.value = 0, 0 + dut.cfg_forward_en.value = forward + dut.load_sel.value, dut.load_addr.value, dut.load_data.value = 0, 0, 0 + await FallingEdge(dut.clk) + for sel, data, count in ((0, imem, IMEM_SIZE // 4), (1, dmem, DMEM_SIZE // 4)): + for i, w in enumerate(words(data, count)): # whole memory: wipes the previous program + dut.load_en.value, dut.load_sel.value, dut.load_addr.value, dut.load_data.value = 1, sel, i, w + await FallingEdge(dut.clk) + dut.load_en.value = 0 + await FallingEdge(dut.clk) + dut.rst_n.value = 1 # first rising edge after this executes the instruction at pc 0 + + +def expected_cycles(instret: int, history, forward: bool = True) -> int: + """Cycles from reset release until the halting retire is visible. + SINGLE: one per instruction. PIPE: the independent timing model (golden/pipemodel.py).""" + if CORE == "SINGLE": + return instret + return pipe_cycles(history, forward) + + +def explain(history, msg): + tail = "\n".join(f" {r}" for r in history[-8:]) + return f"{msg}\n last ISS retires:\n{tail}" + + +async def lockstep(dut, imem: bytes, dmem: bytes = b"", expect_fault: bool | None = None, forward: int = 1): + """Run RTL and ISS together to halt (or fault). Returns (iss, rtl_console, cycles).""" + await reset_and_load(dut, imem, dmem, forward) + iss = Iss(imem, dmem) + history, console, cycles, faulted = [], bytearray(), 0, False + + while True: + await RisingEdge(dut.clk) + await ReadOnly() + cycles += 1 + assert cycles <= MAX_CYCLES, explain(history, f"no halt after {MAX_CYCLES} cycles") + if int(dut.console_valid.value): + console.append(int(dut.console_data.value)) + + if int(dut.fault.value): # RTL refused an instruction: the ISS must raise on the same one + assert not int(dut.rvfi_valid.value), "RTL retired and faulted in the same cycle" + try: + iss.step() + except IssError: + faulted = True + break + raise AssertionError(explain(history, f"RTL faulted at pc={int(dut.dbg_pc.value):#x}, ISS executed it")) + + if int(dut.rvfi_valid.value): + try: + exp = iss.step() + except IssError as exc: + raise AssertionError(explain(history, f"RTL retired pc={int(dut.rvfi_pc.value):#x} but ISS raised: {exc}")) + for sig, attr, cyc_dep in FIELDS: + if cyc_dep and exp.cycle_dependent: + continue + got, want = int(getattr(dut, sig).value), int(getattr(exp, attr)) + assert got == want, explain( + history + [exp], f"retire #{exp.order} {attr}: RTL={got:#x} ISS={want:#x}\n at {exp}") + assert int(dut.rvfi_cycle_dep.value) == int(exp.cycle_dependent), explain(history + [exp], "cycle_dep flag") + history.append(exp) + if exp.halt: + break + else: + assert not iss.halted, "ISS halted but RTL never reported the halting retire" + + if expect_fault is not None: + assert faulted == expect_fault, f"expected fault={expect_fault}, got {faulted}" + + # after a halt/fault the core must be frozen + pc, regs = int(dut.dbg_pc.value), int(dut.dbg_regs.value) + for _ in range(4): + await RisingEdge(dut.clk) + await ReadOnly() + assert not int(dut.rvfi_valid.value) and not int(dut.console_valid.value) + assert int(dut.dbg_pc.value) == pc and int(dut.dbg_regs.value) == regs, "core moved after halt" + assert int(dut.halted.value) == int(not faulted) and int(dut.fault.value) == int(faulted) + + # architectural state matches + # Registers last written by a cycle-dependent instruction (rdcycle, rdtime, CYCLE MMIO) hold timing + # values the ISS cannot know; they are masked in the per-retire compare and here. + timing = set() + for r in history: + if r.rd_addr: + (timing.add if r.cycle_dependent else timing.discard)(r.rd_addr) + got_regs = int(dut.dbg_regs.value) + got = [(got_regs >> (32 * i)) & 0xFFFFFFFF for i in range(32)] + diff = [i for i in range(32) if got[i] != iss.regs[i] and i not in timing] + assert not diff, "register file differs: " + ", ".join( + f"x{i} RTL={got[i]:#x} ISS={iss.regs[i]:#x}" for i in diff) + assert bytes(console) == bytes(iss.console), f"console: RTL={bytes(console)!r} ISS={bytes(iss.console)!r}" + if not faulted: + assert bool(int(dut.tohost_valid.value)) == (iss.tohost is not None) + if iss.tohost is not None: + assert int(dut.tohost_data.value) == iss.tohost + assert int(dut.dbg_pc.value) == iss.pc + assert iss.instret == int(dut.dbg_instret.value), ( + f"instret: ISS={iss.instret} RTL={int(dut.dbg_instret.value)}") + want = expected_cycles(iss.instret, history, bool(forward)) + assert cycles == want, ( + f"{CORE} forward={forward} cycle count: RTL={cycles} model={want} (instret={iss.instret})") + return iss, bytes(console), cycles + + diff --git a/sim/perf_baseline.json b/sim/perf_baseline.json new file mode 100644 index 0000000..11b1b67 --- /dev/null +++ b/sim/perf_baseline.json @@ -0,0 +1,86 @@ +{ + "fib": { + "instret": 3190, + "single": 3190, + "pipe_fwd": 4758, + "pipe_nofwd": 4824 + }, + "hazard_free/alu_zoo": { + "instret": 265, + "single": 265, + "pipe_fwd": 269, + "pipe_nofwd": 269 + }, + "hazard_free/branches": { + "instret": 229, + "single": 229, + "pipe_fwd": 263, + "pipe_nofwd": 263 + }, + "hazard_free/bubble_sort": { + "instret": 749, + "single": 749, + "pipe_fwd": 863, + "pipe_nofwd": 863 + }, + "hazard_free/calls": { + "instret": 75, + "single": 75, + "pipe_fwd": 97, + "pipe_nofwd": 97 + }, + "hazard_free/console_and_csr": { + "instret": 214, + "single": 214, + "pipe_fwd": 218, + "pipe_nofwd": 218 + }, + "hazard_free/fib": { + "instret": 361, + "single": 361, + "pipe_fwd": 407, + "pipe_nofwd": 407 + }, + "hazard_free/mem_raw_adjacent": { + "instret": 62, + "single": 62, + "pipe_fwd": 66, + "pipe_nofwd": 66 + }, + "hazard_free/mem_zoo": { + "instret": 316, + "single": 316, + "pipe_fwd": 320, + "pipe_nofwd": 320 + }, + "hazard_free/memcpy_sum": { + "instret": 1164, + "single": 1164, + "pipe_fwd": 1292, + "pipe_nofwd": 1292 + }, + "hello": { + "instret": 91, + "single": 91, + "pipe_fwd": 150, + "pipe_nofwd": 171 + }, + "memcpy": { + "instret": 552, + "single": 552, + "pipe_fwd": 816, + "pipe_nofwd": 1042 + }, + "primes": { + "instret": 1963, + "single": 1963, + "pipe_fwd": 2836, + "pipe_nofwd": 3652 + }, + "sort": { + "instret": 1216, + "single": 1216, + "pipe_fwd": 1744, + "pipe_nofwd": 2117 + } +} diff --git a/sim/programs/common.h b/sim/programs/common.h new file mode 100644 index 0000000..d5746c0 --- /dev/null +++ b/sim/programs/common.h @@ -0,0 +1,70 @@ +// Shared macros for the TinyCPU asm programs (included by every sim/programs/*.S). +// CONSOLE 0x0002_0000: store a byte to print it TOHOST 0x0002_0004: store 1 = PASS +#ifndef TINYCPU_COMMON_H +#define TINYCPU_COMMON_H + +#define CONSOLE_HI 0x20 /* lui rX, 0x20 -> 0x0002_0000 */ + +// Entry point at address 0; sp = top of the 4 KiB DMEM. +#define PROGRAM_BEGIN \ + .section .text.init; \ + .globl _start; \ +_start: \ + lui sp, 0x11; + +// Print the character in `reg` (clobbers t6). +#define PUTC(reg) \ + lui t6, CONSOLE_HI; \ + sb reg, 0(t6); + +// Print an immediate character (clobbers t5, t6). +#define PUTCI(ch) \ + li t5, ch; \ + PUTC(t5) + +// Halt with a status code: 1 = PASS, anything else = FAIL. +#define EXIT(code) \ + li t5, code; \ + lui t6, CONSOLE_HI; \ + sw t5, 4(t6); \ +9999: j 9999b; + +// print_str: a0 = address of NUL-terminated string. Clobbers t0, t1. +#define PRINT_STR_ROUTINE \ +print_str: \ + lui t1, CONSOLE_HI; \ +1: lbu t0, 0(a0); \ + beqz t0, 2f; \ + sb t0, 0(t1); \ + addi a0, a0, 1; \ + j 1b; \ +2: ret; + +// print_uint: a0 = unsigned value, printed in decimal (no newline). Uses 16 bytes of stack. +// Division by 10 is repeated subtraction (no M extension), so keep values small. +#define PRINT_UINT_ROUTINE \ +print_uint: \ + addi sp, sp, -16; \ + li t3, 0; \ + li t4, 10; \ +1: li t0, 0; \ +2: bltu a0, t4, 3f; \ + addi a0, a0, -10; \ + addi t0, t0, 1; \ + j 2b; \ +3: addi a0, a0, 48; \ + add t1, sp, t3; \ + sb a0, 0(t1); \ + addi t3, t3, 1; \ + mv a0, t0; \ + bnez a0, 1b; \ +4: addi t3, t3, -1; \ + add t1, sp, t3; \ + lbu a0, 0(t1); \ + lui t2, CONSOLE_HI; \ + sb a0, 0(t2); \ + bnez t3, 4b; \ + addi sp, sp, 16; \ + ret; + +#endif diff --git a/sim/programs/expected.json b/sim/programs/expected.json new file mode 100644 index 0000000..b7c5a0c --- /dev/null +++ b/sim/programs/expected.json @@ -0,0 +1,8 @@ +{ + "_comment": "console = exact expected output; instret = retired instructions (the single-cycle core's cycle count). Pinned: change only when a program changes.", + "hello": { "console": "Hello, TinyCPU!\n", "instret": 91 }, + "fib": { "console": "6765\n", "instret": 3190 }, + "sort": { "console": "1 3 5 7 11 18 27 42 50 64 85 99 \n", "instret": 1216 }, + "memcpy": { "console": "OK\n", "instret": 552 }, + "primes": { "console": "2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 \n", "instret": 1963 } +} diff --git a/sim/programs/fib.S b/sim/programs/fib.S new file mode 100644 index 0000000..6046b93 --- /dev/null +++ b/sim/programs/fib.S @@ -0,0 +1,19 @@ +// Iterative Fibonacci: prints fib(20) = 6765. +#include "common.h" + +PROGRAM_BEGIN + li s0, 20 // n + li a1, 0 // fib(i) + li a2, 1 // fib(i+1) +1: beqz s0, 2f + add a3, a1, a2 + mv a1, a2 + mv a2, a3 + addi s0, s0, -1 + j 1b +2: mv a0, a1 + call print_uint + PUTCI('\n') + EXIT(1) + +PRINT_UINT_ROUTINE diff --git a/sim/programs/hello.S b/sim/programs/hello.S new file mode 100644 index 0000000..646500b --- /dev/null +++ b/sim/programs/hello.S @@ -0,0 +1,12 @@ +// Prints a greeting through the CONSOLE MMIO register, then reports PASS. +#include "common.h" + +PROGRAM_BEGIN + la a0, msg + call print_str + EXIT(1) + +PRINT_STR_ROUTINE + + .section .rodata +msg: .asciz "Hello, TinyCPU!\n" diff --git a/sim/programs/memcpy.S b/sim/programs/memcpy.S new file mode 100644 index 0000000..e963041 --- /dev/null +++ b/sim/programs/memcpy.S @@ -0,0 +1,46 @@ +// Copies 16 words with a byte-granular memcpy, verifies them, and prints OK or FAIL. +#include "common.h" + +PROGRAM_BEGIN + la a0, dst + la a1, src + li a2, 64 + call memcpy + // compare word by word + la t0, dst + la t1, src + li t2, 16 +1: beqz t2, ok + lw t3, 0(t0) + lw t4, 0(t1) + bne t3, t4, bad + addi t0, t0, 4 + addi t1, t1, 4 + addi t2, t2, -1 + j 1b +ok: la a0, s_ok + call print_str + EXIT(1) +bad: la a0, s_bad + call print_str + EXIT(3) + +// memcpy(a0 = dst, a1 = src, a2 = bytes) +memcpy: beqz a2, 2f +1: lbu t0, 0(a1) + sb t0, 0(a0) + addi a0, a0, 1 + addi a1, a1, 1 + addi a2, a2, -1 + bnez a2, 1b +2: ret + +PRINT_STR_ROUTINE + + .data + .align 2 +src: .word 0x00000001, 0xdeadbeef, 0x80000000, 0x7fffffff, 0xffffffff, 0x12345678, 0x0badf00d, 0xcafebabe + .word 0x0f0f0f0f, 0xf0f0f0f0, 0x00ff00ff, 0xff00ff00, 0x13579bdf, 0x2468ace0, 0x55aa55aa, 0xaa55aa55 +dst: .space 64 +s_ok: .asciz "OK\n" +s_bad: .asciz "FAIL\n" diff --git a/sim/programs/primes.S b/sim/programs/primes.S new file mode 100644 index 0000000..6b7db61 --- /dev/null +++ b/sim/programs/primes.S @@ -0,0 +1,53 @@ +// Sieve of Eratosthenes up to 60: prints every prime. +#include "common.h" + +#define LIMIT 60 + +PROGRAM_BEGIN + la s0, sieve // byte array in .bss, zero-initialised by the loop below + li s1, LIMIT + li t0, 0 +1: bgt t0, s1, 2f // clear sieve[0..LIMIT] + add t1, s0, t0 + sb zero, 0(t1) + addi t0, t0, 1 + j 1b +2: li s2, 2 // p +3: + mv t2, s2 // t2 = p*p via repeated addition + li t3, 0 + mv t4, s2 +4: beqz t4, 5f + add t3, t3, t2 + addi t4, t4, -1 + j 4b +5: bgt t3, s1, 8f // p*p > LIMIT: done sieving + add t1, s0, s2 + lbu t5, 0(t1) + bnez t5, 7f // p already composite + mv t0, t3 // mark p*p, p*p+p, ... +6: bgt t0, s1, 7f + add t1, s0, t0 + li t5, 1 + sb t5, 0(t1) + add t0, t0, s2 + j 6b +7: addi s2, s2, 1 + j 3b +8: li s2, 2 // print primes +9: bgt s2, s1, 11f + add t1, s0, s2 + lbu t5, 0(t1) + bnez t5, 10f + mv a0, s2 + call print_uint + PUTCI(' ') +10: addi s2, s2, 1 + j 9b +11: PUTCI('\n') + EXIT(1) + +PRINT_UINT_ROUTINE + + .bss +sieve: .space LIMIT + 1 diff --git a/sim/programs/sort.S b/sim/programs/sort.S new file mode 100644 index 0000000..68bf110 --- /dev/null +++ b/sim/programs/sort.S @@ -0,0 +1,39 @@ +// Bubble sort of 12 words in DMEM, then prints them space-separated. +#include "common.h" + +PROGRAM_BEGIN + la s0, array + li s1, 12 // n + // for (pass = n-1; pass > 0; pass--) + addi s2, s1, -1 +1: blez s2, 5f + li t0, 0 // i + mv t1, s0 // &array[i] +2: bge t0, s2, 4f + lw a1, 0(t1) + lw a2, 4(t1) + ble a1, a2, 3f + sw a2, 0(t1) + sw a1, 4(t1) +3: addi t0, t0, 1 + addi t1, t1, 4 + j 2b +4: addi s2, s2, -1 + j 1b +5: li s2, 0 // print loop +6: bge s2, s1, 8f + slli t0, s2, 2 + add t0, t0, s0 + lw a0, 0(t0) + call print_uint + PUTCI(' ') + addi s2, s2, 1 + j 6b +8: PUTCI('\n') + EXIT(1) + +PRINT_UINT_ROUTINE + + .data + .align 2 +array: .word 42, 7, 99, 3, 18, 64, 1, 27, 85, 11, 50, 5 diff --git a/sim/run.sh b/sim/run.sh new file mode 100644 index 0000000..07fb13b --- /dev/null +++ b/sim/run.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# cocotb suites. Usage: bash sim/run.sh unit | lockstep | all (needs `source scripts/env.sh`) +# Each suite gets its own SIM_BUILD: cocotb does not notice a TOPLEVEL change in a shared dir. +set -euo pipefail +cd "$(dirname "$0")" +PKG=../rtl/tiny_cpu_pkg.sv +FAILED=() + +run() { # run (env EXTRA_COMPILE_ARGS, SIM_TAG optional) + local mod=$1 top=$2; shift 2 + local srcs="" + for f in "$@"; do srcs+=" ../rtl/$f"; done + echo "=== $mod ($top)" + if ! make COCOTB_TEST_MODULES="$mod" TOPLEVEL="$top" VERILOG_SOURCES="$PKG$srcs" WAVES=0 \ + SIM_BUILD="sim_build/$mod${SIM_TAG:-}" EXTRA_ARGS="${EXTRA_COMPILE_ARGS:-}" COCOTB_RESULTS_FILE="results_$mod.xml" > "sim_build_$mod.log" 2>&1; then + FAILED+=("$mod"); tail -n 25 "sim_build_$mod.log" + elif grep -q 'FAIL=[1-9]' "sim_build_$mod.log"; then + FAILED+=("$mod"); grep -E "FAIL|Error|assert" "sim_build_$mod.log" | head -n 20 + else + grep -E "TESTS=" "sim_build_$mod.log" | sed 's/^ *\*\* //; s/ *\*\*$//; s/ */ /g' + fi + rm -f "sim_build_$mod.log" "results_$mod.xml" +} + +unit() { + run test_counter counter spike/counter.sv # Phase 0 toolchain check + run test_alu alu alu.sv + run test_imm_gen imm_gen imm_gen.sv + run test_branch_unit branch_unit branch_unit.sv + run test_lsu lsu lsu.sv + run test_decoder decoder decoder.sv + run test_regfile regfile regfile.sv + SIM_TAG=_nobypass EXTRA_COMPILE_ARGS="-GBYPASS=0" run test_regfile_nobypass regfile regfile.sv + run test_csr_counters csr_counters csr_counters.sv + run test_mmio mmio mmio.sv + run test_forward_unit forward_unit forward_unit.sv + run test_hazard_unit hazard_unit hazard_unit.sv + run test_mem_sync mem_sync mem_sync.sv + SIM_TAG=_oldread EXTRA_COMPILE_ARGS="-GREAD_NEW=0" MEM_READ_NEW=0 run test_mem_sync mem_sync mem_sync.sv +} + +lockstep() { + run test_core_lockstep tiny_cpu_top alu.sv imm_gen.sv branch_unit.sv lsu.sv decoder.sv regfile.sv \ + csr_counters.sv mmio.sv mem_comb.sv mem_sync.sv forward_unit.sv hazard_unit.sv core_single.sv core_pipe.sv tiny_cpu_top.sv +} + +pipe() { + TINYCPU_CORE=PIPE EXTRA_COMPILE_ARGS="-G'CORE=\"PIPE\"'" SIM_TAG=_pipe run test_pipe_lockstep tiny_cpu_top \ + alu.sv imm_gen.sv branch_unit.sv lsu.sv decoder.sv regfile.sv csr_counters.sv mmio.sv mem_comb.sv \ + mem_sync.sv forward_unit.sv hazard_unit.sv core_single.sv core_pipe.sv tiny_cpu_top.sv + TINYCPU_CORE=PIPE EXTRA_COMPILE_ARGS="-G'CORE=\"PIPE\"'" SIM_TAG=_pipe run test_pipe_hazards tiny_cpu_top \ + alu.sv imm_gen.sv branch_unit.sv lsu.sv decoder.sv regfile.sv csr_counters.sv mmio.sv mem_comb.sv \ + mem_sync.sv forward_unit.sv hazard_unit.sv core_single.sv core_pipe.sv tiny_cpu_top.sv +} + +case "${1:-all}" in + unit) unit ;; + lockstep) lockstep ;; + pipe) pipe ;; + all) unit; lockstep; pipe ;; + *) echo "usage: $0 unit|lockstep|pipe|all" >&2; exit 2 ;; +esac + +if [ ${#FAILED[@]} -gt 0 ]; then echo "FAILED: ${FAILED[*]}"; exit 1; fi +echo "ALL SUITES PASSED" diff --git a/sim/test_alu.py b/sim/test_alu.py new file mode 100644 index 0000000..ebe11c3 --- /dev/null +++ b/sim/test_alu.py @@ -0,0 +1,39 @@ +import random + +import cocotb +from cocotb.triggers import Timer + +from consts import ALU +from golden.iss import _alu # the golden model's ALU is the oracle + +EDGE = [0, 1, 2, 31, 32, 33, 0x7FFFFFFF, 0x80000000, 0x80000001, 0xFFFFFFFF, 0xFFFFFFFE, 0x55555555, 0xAAAAAAAA] + + +def ref(op, a, b): + return b if op == "COPY_B" else _alu(op.lower(), a, b) + + +async def check(dut, op, a, b): + dut.a.value, dut.b.value, dut.op.value = a, b, ALU[op] + await Timer(1, "ns") + got = int(dut.y.value) + assert got == ref(op, a, b), f"{op}({a:#x}, {b:#x}) = {got:#x}, want {ref(op, a, b):#x}" + + +@cocotb.test() +async def every_op_edge_and_random(dut): + rng = random.Random(42) + for op in ALU: + pairs = [(a, b) for a in EDGE for b in EDGE] + pairs += [(rng.getrandbits(32), rng.getrandbits(32)) for _ in range(300)] + for a, b in pairs: + await check(dut, op, a, b) + + +@cocotb.test() +async def shifts_use_only_low_five_bits_of_b(dut): + rng = random.Random(7) + for op in ("SLL", "SRL", "SRA"): + for sh in range(64): # shamt 32..63 must wrap + for a in (1, 0x80000000, 0xDEADBEEF, rng.getrandbits(32)): + await check(dut, op, a, sh | (rng.getrandbits(27) << 5)) diff --git a/sim/test_branch_unit.py b/sim/test_branch_unit.py new file mode 100644 index 0000000..508a36e --- /dev/null +++ b/sim/test_branch_unit.py @@ -0,0 +1,26 @@ +import itertools + +import cocotb +from cocotb.triggers import Timer + +from golden.decode import s32 + +VALS = [0, 1, 2, 0x7FFFFFFF, 0x80000000, 0x80000001, 0xFFFFFFFE, 0xFFFFFFFF, 0x12345678] +REF = { + 0: lambda a, b: a == b, + 1: lambda a, b: a != b, + 4: lambda a, b: s32(a) < s32(b), + 5: lambda a, b: s32(a) >= s32(b), + 6: lambda a, b: a < b, + 7: lambda a, b: a >= b, + 2: lambda a, b: False, # reserved encodings never take + 3: lambda a, b: False, +} + + +@cocotb.test() +async def all_conditions(dut): + for f3, (a, b) in itertools.product(REF, itertools.product(VALS, VALS)): + dut.a.value, dut.b.value, dut.funct3.value = a, b, f3 + await Timer(1, "ns") + assert int(dut.taken.value) == int(REF[f3](a, b)), f"funct3={f3} a={a:#x} b={b:#x}" diff --git a/sim/test_core_lockstep.py b/sim/test_core_lockstep.py new file mode 100644 index 0000000..d81785c --- /dev/null +++ b/sim/test_core_lockstep.py @@ -0,0 +1,109 @@ +"""core_single vs golden ISS over every riscv-tests image and example program, plus directed cases. + +Build the images first: make -C sw all. REQUIRE_RISCV_TESTS=1 turns a missing build into a failure. +(The 5-stage core has its own file: test_pipe_lockstep.py.) +""" + +import os +from pathlib import Path + +import cocotb + +from lockstep_lib import EXPECTED, IMEM_SIZE, e, images, lockstep, prog_bytes, stem_path + +# --------------------------------------------------------------------------- images +@cocotb.test() +async def images_present(dut): + if not images(): + msg = "no images in sw/build (run `make -C sw all`)" + assert os.environ.get("REQUIRE_RISCV_TESTS") != "1", msg + dut._log.warning(msg) + + +NAMES = images() +if NAMES: + + @cocotb.parametrize(name=NAMES) + async def test_image(dut, name): + stem = stem_path(name) + imem, dmem = (Path(str(stem) + ".imem.bin").read_bytes(), Path(str(stem) + ".dmem.bin").read_bytes()) + iss, console, _ = await lockstep(dut, imem, dmem, expect_fault=False) + if name.startswith("rv32ui"): + assert iss.passed, f"{name}: failed test case #{(iss.tohost or 0) >> 1}" + else: + assert iss.passed + assert console.decode() == EXPECTED[name]["console"] + assert iss.instret == EXPECTED[name]["instret"] + + +# --------------------------------------------------------------------------- directed +@cocotb.test() +async def ecall_and_ebreak_halt_after_retiring(dut): + for halt_insn in (e("ecall"), e("ebreak")): + iss, _, cycles = await lockstep(dut, prog_bytes(e("addi", 1, 0, imm=7), halt_insn, e("addi", 1, 0, imm=9)), + expect_fault=False) + assert cycles == 2 and iss.regs[1] == 7 and iss.tohost is None + + +@cocotb.test() +async def x0_writes_and_self_dependent_instructions(dut): + """lw x1,0(x1)-style hazards that only exist in a single-cycle core with a (wrong) bypass.""" + prog = [e("lui", 1, imm=0x10000), e("addi", 2, 0, imm=0x55), e("sw", 0, 1, 2, imm=0), + e("addi", 1, 1, imm=0), e("lw", 1, 1, imm=0), # x1 = mem[x1] using the OLD x1 + e("addi", 0, 1, imm=5), e("add", 3, 0, 1), e("jalr", 1, 1, imm=0)] + prog[-1] = e("addi", 4, 1, imm=1) + await lockstep(dut, prog_bytes(*prog, e("ecall")), expect_fault=False) + + +@cocotb.test() +async def faults_stop_the_core_without_retiring(dut): + cases = { + "illegal instruction": [e("addi", 1, 0, imm=5), 0x00000000], + "csrrs with rs1 != x0": [e("csrrs", 1, 1, csr=0xC00)], + "unknown csr": [e("csrrs", 1, 0, csr=0x300)], + "load from IMEM": [e("lw", 1, 0, imm=0)], + "store to IMEM": [e("sw", 0, 0, 0, imm=0)], + "unmapped load": [e("lui", 1, imm=0x80000000), e("lw", 2, 1, imm=0)], + "tohost byte store": [e("lui", 1, imm=0x20000), e("sb", 0, 1, 0, imm=4)], + "store to CYCLE": [e("lui", 1, imm=0x20000), e("sw", 0, 1, 0, imm=8)], + "misaligned jal": [e("jal", 0, imm=6)], + "misaligned taken branch": [e("beq", 0, 0, 0, imm=2)], + "jalr to odd word": [e("addi", 1, 0, imm=6), e("jalr", 0, 1, imm=0)], + "jump past end of IMEM": [e("lui", 1, imm=0x1000), e("jalr", 0, 1, imm=0)], + "fall off end of IMEM": [e("jal", 0, imm=IMEM_SIZE - 4)], + } + for label, prog in cases.items(): + try: + await lockstep(dut, prog_bytes(*prog), expect_fault=True) + except AssertionError as exc: + raise AssertionError(f"[{label}] {exc}") from exc + + +@cocotb.test() +async def untaken_misaligned_branch_is_fine(dut): + prog = [e("addi", 1, 0, imm=1), e("beq", 0, 1, 0, imm=2), e("addi", 2, 0, imm=2), e("ecall")] + iss, _, _ = await lockstep(dut, prog_bytes(*prog), expect_fault=False) + assert iss.regs[2] == 2 + + +@cocotb.test() +async def misaligned_loads_stores_are_forced_into_alignment(dut): + prog = [e("lui", 1, imm=0x10000), e("lui", 2, imm=0x11223000), e("addi", 2, 2, imm=0x344), + e("sw", 0, 1, 2, imm=0), e("lw", 3, 1, imm=1), e("lh", 4, 1, imm=3), e("sh", 0, 1, 2, imm=1), + e("sw", 0, 1, 2, imm=3), e("lw", 5, 1, imm=0), e("ecall")] + await lockstep(dut, prog_bytes(*prog), expect_fault=False) + + +@cocotb.test() +async def jalr_clears_bit0_of_the_target(dut): + """No official rv32ui test jumps to an odd address, so pin it here: (rs1 + imm) & ~1.""" + # odd base register, even offset -> odd sum + prog = [e("addi", 1, 0, imm=0x11), e("jalr", 2, 1, imm=0), e("addi", 3, 0, imm=99), e("addi", 3, 0, imm=98), + e("ecall")] + iss, _, _ = await lockstep(dut, prog_bytes(*prog), expect_fault=False) + assert iss.regs[2] == 8 and iss.regs[3] == 0 # linked to pc+4, skipped both addi + # even base, odd offset -> odd sum + prog = [e("addi", 1, 0, imm=0x10), e("jalr", 2, 1, imm=1), e("addi", 3, 0, imm=99), e("addi", 3, 0, imm=98), + e("ecall")] + iss, _, _ = await lockstep(dut, prog_bytes(*prog), expect_fault=False) + assert iss.regs[3] == 0 diff --git a/sim/test_csr_counters.py b/sim/test_csr_counters.py new file mode 100644 index 0000000..0dfa6f7 --- /dev/null +++ b/sim/test_csr_counters.py @@ -0,0 +1,48 @@ +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import FallingEdge, Timer + + +async def setup(dut): + cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start()) + dut.retire.value, dut.csr_addr.value, dut.rst_n.value = 0, 0, 0 + await Timer(12, "ns") + dut.rst_n.value = 1 + await FallingEdge(dut.clk) + + +async def csr(dut, addr): + dut.csr_addr.value = addr + await Timer(1, "ns") + return int(dut.csr_rdata.value) + + +@cocotb.test() +async def cycle_counts_every_clock_instret_only_on_retire(dut): + await setup(dut) + for _ in range(10): + await FallingEdge(dut.clk) + assert await csr(dut, 0xC00) == 10 + assert await csr(dut, 0xC01) == 10 # time aliases cycle + assert await csr(dut, 0xC02) == 0 + assert int(dut.cycle_lo.value) == 10 + dut.retire.value = 1 + for _ in range(4): + await FallingEdge(dut.clk) + dut.retire.value = 0 + for _ in range(3): + await FallingEdge(dut.clk) + assert await csr(dut, 0xC02) == 4 == int(dut.instret_lo.value) + assert await csr(dut, 0xC00) == 17 + assert await csr(dut, 0xC80) == await csr(dut, 0xC81) == await csr(dut, 0xC82) == 0 + assert await csr(dut, 0x300) == 0 # unknown CSR reads zero (the decoder never lets it through) + + +@cocotb.test() +async def reset_clears_counters(dut): + await setup(dut) + for _ in range(5): + await FallingEdge(dut.clk) + dut.rst_n.value = 0 + await Timer(1, "ns") + assert await csr(dut, 0xC00) == 0 diff --git a/sim/test_decoder.py b/sim/test_decoder.py new file mode 100644 index 0000000..9f6aacd --- /dev/null +++ b/sim/test_decoder.py @@ -0,0 +1,104 @@ +"""Decoder: control bundle per instruction, and legality identical to the golden ISS.""" + +import random + +import cocotb +from cocotb.triggers import Timer + +from consts import ALU, IMM, SZ, WB +from golden.decode import CSR_NAMES, INSNS, decode, encode + +SIGNALS = ["alu_op", "a_pc", "b_imm", "imm_sel", "reg_we", "wb_sel", "mem_re", "mem_we", "mem_size", + "mem_unsigned", "is_branch", "is_jal", "is_jalr", "uses_rs1", "uses_rs2", "is_halt", "illegal"] +DEFAULT = dict(alu_op=ALU["ADD"], a_pc=0, b_imm=0, imm_sel=IMM["I"], reg_we=0, wb_sel=WB["ALU"], mem_re=0, + mem_we=0, mem_size=SZ["WORD"], mem_unsigned=0, is_branch=0, is_jal=0, is_jalr=0, uses_rs1=0, + uses_rs2=0, is_halt=0, illegal=0) + + +def expected(name): + """Independent description of the control bundle for each instruction.""" + e = dict(DEFAULT) + if name == "lui": + e.update(alu_op=ALU["COPY_B"], b_imm=1, imm_sel=IMM["U"], reg_we=1) + elif name == "auipc": + e.update(a_pc=1, b_imm=1, imm_sel=IMM["U"], reg_we=1) + elif name == "jal": + e.update(imm_sel=IMM["J"], is_jal=1, reg_we=1, wb_sel=WB["PC4"]) + elif name == "jalr": + e.update(b_imm=1, is_jalr=1, reg_we=1, wb_sel=WB["PC4"], uses_rs1=1) + elif name in ("beq", "bne", "blt", "bge", "bltu", "bgeu"): + e.update(imm_sel=IMM["B"], is_branch=1, uses_rs1=1, uses_rs2=1) + elif name in ("lb", "lh", "lw", "lbu", "lhu"): + size = {"b": "BYTE", "h": "HALF", "w": "WORD"}[name[1]] + e.update(b_imm=1, mem_re=1, reg_we=1, wb_sel=WB["MEM"], uses_rs1=1, mem_size=SZ[size], + mem_unsigned=int(name.endswith("u"))) + elif name in ("sb", "sh", "sw"): + size = {"b": "BYTE", "h": "HALF", "w": "WORD"}[name[1]] + e.update(b_imm=1, imm_sel=IMM["S"], mem_we=1, uses_rs1=1, uses_rs2=1, mem_size=SZ[size]) + elif name in ("addi", "slti", "sltiu", "xori", "ori", "andi", "slli", "srli", "srai"): + op = {"addi": "ADD", "slti": "SLT", "sltiu": "SLTU", "xori": "XOR", "ori": "OR", "andi": "AND", + "slli": "SLL", "srli": "SRL", "srai": "SRA"}[name] + e.update(alu_op=ALU[op], b_imm=1, reg_we=1, uses_rs1=1) + elif name in ("add", "sub", "sll", "slt", "sltu", "xor", "srl", "sra", "or", "and"): + e.update(alu_op=ALU[name.upper()], reg_we=1, uses_rs1=1, uses_rs2=1) + elif name == "fence": + pass + elif name in ("ecall", "ebreak"): + e.update(is_halt=1) + elif name == "csrrs": + e.update(reg_we=1, wb_sel=WB["CSR"]) + else: + raise AssertionError(name) + return e + + +def legal(word): + """The golden ISS's notion of an executable instruction.""" + x = decode(word) + if x is None: + return False + return x.rs1 == 0 and x.csr in CSR_NAMES if x.name == "csrrs" else True + + +async def sample(dut, word): + dut.insn.value = word + await Timer(1, "ns") + return {s: int(getattr(dut, s).value) for s in SIGNALS} + + +@cocotb.test() +async def every_instruction_control_bundle(dut): + rng = random.Random(42) + for d in INSNS: + for _ in range(60): + imm = {"I": rng.randint(-2048, 2047), "SH": rng.randrange(32), "S": rng.randint(-2048, 2047), + "B": rng.randrange(-2048, 2048) * 2, "U": rng.randrange(1 << 20) << 12, + "J": rng.randrange(-(1 << 19), 1 << 19) * 2}.get(d.fmt, 0) + word = encode(d.name, rng.randrange(32), rng.randrange(32), rng.randrange(32), imm, + csr=rng.choice(list(CSR_NAMES))) + if d.name == "csrrs": + word = encode("csrrs", rng.randrange(32), 0, csr=rng.choice(list(CSR_NAMES))) + got = await sample(dut, word) + want = expected(d.name) + assert got == want, f"{d.name} {word:#010x}: " + ", ".join( + f"{k}={got[k]} (want {want[k]})" for k in SIGNALS if got[k] != want[k]) + + +@cocotb.test() +async def legality_matches_iss_and_illegal_is_inert(dut): + rng = random.Random(1234) + words = [0x00000000, 0xFFFFFFFF, 0x00000073, 0x00100073, 0x00200073, 0x30200073] + words += [rng.getrandbits(32) for _ in range(4000)] + # single-bit and field mutations of legal instructions probe the funct3/funct7 boundaries + for d in INSNS: + base = d.match | (rng.getrandbits(32) & ~d.mask & ~0x7F & 0xFFFFFFFF) + for bit in range(32): + words.append(base ^ (1 << bit)) + for w in words: + got = await sample(dut, w) + assert got["illegal"] == int(not legal(w)), f"{w:#010x}: illegal={got['illegal']}, ISS legal={legal(w)}" + if not legal(w): + for s in ("reg_we", "mem_re", "mem_we", "is_branch", "is_jal", "is_jalr", "is_halt"): + assert got[s] == 0, f"{w:#010x}: illegal instruction has {s}=1" + else: + assert {k: v for k, v in got.items()} == expected(decode(w).name), f"{w:#010x} {decode(w).name}" diff --git a/sim/test_forward_unit.py b/sim/test_forward_unit.py new file mode 100644 index 0000000..0c15378 --- /dev/null +++ b/sim/test_forward_unit.py @@ -0,0 +1,49 @@ +import itertools + +import cocotb +from cocotb.triggers import Timer + +NONE, EXMEM, MEMWB = 0, 1, 2 + + +def want(en, ex_valid, uses, rs, mm_wr, mm_rd, wb_wr, wb_rd): + if not en or not ex_valid or not uses or rs == 0: + return NONE + if mm_wr and mm_rd == rs: + return EXMEM + if wb_wr and wb_rd == rs: + return MEMWB + return NONE + + +@cocotb.test() +async def exhaustive_over_small_register_space(dut): + regs = (0, 1, 2) + n = 0 + for en, ex_valid, u1, u2, mm_wr, wb_wr in itertools.product((0, 1), repeat=6): + for rs1, rs2, mm_rd, wb_rd in itertools.product(regs, repeat=4): + dut.forward_en.value, dut.ex_valid.value = en, ex_valid + dut.ex_uses_rs1.value, dut.ex_uses_rs2.value = u1, u2 + dut.ex_rs1.value, dut.ex_rs2.value = rs1, rs2 + dut.mm_wr.value, dut.mm_rd.value, dut.wb_wr.value, dut.wb_rd.value = mm_wr, mm_rd, wb_wr, wb_rd + await Timer(1, "ns") + a = want(en, ex_valid, u1, rs1, mm_wr, mm_rd, wb_wr, wb_rd) + b = want(en, ex_valid, u2, rs2, mm_wr, mm_rd, wb_wr, wb_rd) + ctx = f"en={en} v={ex_valid} uses={u1}{u2} rs={rs1},{rs2} mm={mm_wr}:{mm_rd} wb={wb_wr}:{wb_rd}" + assert int(dut.fwd_a.value) == a, f"fwd_a {ctx}" + assert int(dut.fwd_b.value) == b, f"fwd_b {ctx}" + n += 1 + assert n == 64 * 81 + + +@cocotb.test() +async def the_nearer_producer_wins_and_x0_never_forwards(dut): + dut.forward_en.value = dut.ex_valid.value = dut.ex_uses_rs1.value = dut.ex_uses_rs2.value = 1 + dut.ex_rs1.value, dut.ex_rs2.value = 7, 0 + dut.mm_wr.value = dut.wb_wr.value = 1 + dut.mm_rd.value = dut.wb_rd.value = 7 + await Timer(1, "ns") + assert int(dut.fwd_a.value) == EXMEM # both match: the younger result (EX/MEM) wins + dut.mm_rd.value = dut.wb_rd.value = 0 + await Timer(1, "ns") + assert int(dut.fwd_b.value) == NONE # x0 diff --git a/sim/test_hazard_unit.py b/sim/test_hazard_unit.py new file mode 100644 index 0000000..1d41b15 --- /dev/null +++ b/sim/test_hazard_unit.py @@ -0,0 +1,37 @@ +import itertools + +import cocotb +from cocotb.triggers import Timer + + +def want(fwd, redirect, id_valid, u1, u2, r1, r2, ex_wr, ex_late, ex_rd, mm_wr, mm_rd): + def needs(u, rs, rd): + return bool(u and rs != 0 and rs == rd) + + ex_hit = ex_wr and (needs(u1, r1, ex_rd) or needs(u2, r2, ex_rd)) + mm_hit = mm_wr and (needs(u1, r1, mm_rd) or needs(u2, r2, mm_rd)) + live = id_valid and not redirect + load_use = live and ex_hit and ex_late + stall = live and ((ex_hit and ex_late) if fwd else (ex_hit or mm_hit)) + return int(stall), int(load_use) + + +@cocotb.test() +async def exhaustive(dut): + regs = (0, 1, 2) + n = 0 + for fwd, redirect, id_valid, u1, u2, ex_wr, ex_late, mm_wr in itertools.product((0, 1), repeat=8): + for r1, r2, ex_rd, mm_rd in itertools.product(regs, repeat=4): + dut.forward_en.value, dut.redirect.value, dut.id_valid.value = fwd, redirect, id_valid + dut.id_uses_rs1.value, dut.id_uses_rs2.value = u1, u2 + dut.id_rs1.value, dut.id_rs2.value = r1, r2 + dut.ex_wr.value, dut.ex_late.value, dut.ex_rd.value = ex_wr, ex_late, ex_rd + dut.mm_wr.value, dut.mm_rd.value = mm_wr, mm_rd + await Timer(1, "ns") + s, lu = want(fwd, redirect, id_valid, u1, u2, r1, r2, ex_wr, ex_late, ex_rd, mm_wr, mm_rd) + ctx = (f"fwd={fwd} redir={redirect} v={id_valid} uses={u1}{u2} id=({r1},{r2}) " + f"ex={ex_wr}/late{ex_late}:{ex_rd} mm={mm_wr}:{mm_rd}") + assert int(dut.stall.value) == s, f"stall {ctx}" + assert int(dut.load_use.value) == lu, f"load_use {ctx}" + n += 1 + assert n == 256 * 81 diff --git a/sim/test_imm_gen.py b/sim/test_imm_gen.py new file mode 100644 index 0000000..3d84b1a --- /dev/null +++ b/sim/test_imm_gen.py @@ -0,0 +1,30 @@ +import random + +import cocotb +from cocotb.triggers import Timer + +from consts import IMM +from golden.decode import imm_b, imm_i, imm_j, imm_s, imm_u + +REF = {"I": imm_i, "S": imm_s, "B": imm_b, "U": imm_u, "J": imm_j} + + +@cocotb.test() +async def every_format(dut): + rng = random.Random(42) + words = [0, 0xFFFFFFFF, 0x80000000, 0x7FFFFFFF, 0x00000FFF, 0xFFFFF000, 0x7FF00000, 0x80000F80] + words += [rng.getrandbits(32) for _ in range(2000)] + for fmt, code in IMM.items(): + for w in words: + dut.insn.value, dut.sel.value = w, code + await Timer(1, "ns") + want = REF[fmt](w) & 0xFFFFFFFF + assert int(dut.imm.value) == want, f"{fmt} {w:#010x}: got {int(dut.imm.value):#x}, want {want:#x}" + + +@cocotb.test() +async def undefined_select_gives_zero(dut): + for sel in (5, 6, 7): + dut.insn.value, dut.sel.value = 0xFFFFFFFF, sel + await Timer(1, "ns") + assert int(dut.imm.value) == 0 diff --git a/sim/test_lsu.py b/sim/test_lsu.py new file mode 100644 index 0000000..e01a250 --- /dev/null +++ b/sim/test_lsu.py @@ -0,0 +1,54 @@ +"""LSU vs the golden ISS: same lane/mask/extension conventions for every size x offset.""" + +import itertools +import random + +import cocotb +from cocotb.triggers import Timer + +from consts import SZ +from golden.iss import DMEM_BASE, Iss + +NAME = {("BYTE", False): "lb", ("BYTE", True): "lbu", ("HALF", False): "lh", ("HALF", True): "lhu", + ("WORD", False): "lw", ("WORD", True): "lw"} +STORE = {"BYTE": "sb", "HALF": "sh", "WORD": "sw"} + + +@cocotb.test() +async def loads_match_iss(dut): + rng = random.Random(42) + iss = Iss() + for (size, uns), off in itertools.product(NAME, range(4)): + for _ in range(40): + word = rng.getrandbits(32) + base = DMEM_BASE + 16 + iss.dmem[16:20] = word.to_bytes(4, "little") + want_val, want_word_addr, want_mask, want_lanes, want_mis = iss._load(NAME[(size, uns)], base + off, 0, 0) + dut.addr.value, dut.size.value = base + off, SZ[size] + dut.is_unsigned.value, dut.store_data.value, dut.mem_word.value = int(uns), 0, word + await Timer(1, "ns") + ctx = f"{NAME[(size, uns)]} off={off} word={word:#x}" + assert int(dut.word_addr.value) == want_word_addr, ctx + assert int(dut.lane_mask.value) == want_mask, ctx + assert int(dut.rdata_lanes.value) == want_lanes, ctx + assert int(dut.load_value.value) == want_val, f"{ctx}: {int(dut.load_value.value):#x} != {want_val:#x}" + assert bool(int(dut.misaligned.value)) == want_mis, ctx + + +@cocotb.test() +async def stores_match_iss(dut): + rng = random.Random(43) + for size, off in itertools.product(STORE, range(4)): + for _ in range(40): + val = rng.getrandbits(32) + iss = Iss() + iss.dmem[16:20] = bytes(4) + want_word_addr, want_mask, want_wdata, want_mis = iss._store(STORE[size], DMEM_BASE + 16 + off, val, 0, 0) + dut.addr.value, dut.size.value = DMEM_BASE + 16 + off, SZ[size] + dut.is_unsigned.value, dut.store_data.value, dut.mem_word.value = 0, val, 0 + await Timer(1, "ns") + ctx = f"{STORE[size]} off={off} val={val:#x}" + assert int(dut.word_addr.value) == want_word_addr, ctx + assert int(dut.lane_mask.value) == want_mask, ctx + assert int(dut.wdata.value) == want_wdata, f"{ctx}: {int(dut.wdata.value):#x} != {want_wdata:#x}" + assert bool(int(dut.misaligned.value)) == want_mis, ctx diff --git a/sim/test_mem_sync.py b/sim/test_mem_sync.py new file mode 100644 index 0000000..ce09cdb --- /dev/null +++ b/sim/test_mem_sync.py @@ -0,0 +1,83 @@ +import random + +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import FallingEdge, Timer + +READ_NEW = int(__import__("os").environ.get("MEM_READ_NEW", "1")) + + +async def setup(dut): + cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start()) + dut.re.value, dut.raddr.value, dut.wmask.value = 1, 0, 0 + dut.waddr.value = dut.wdata.value = dut.ld_en.value = dut.ld_addr.value = dut.ld_data.value = 0 + await FallingEdge(dut.clk) + + +async def edge(dut, **kw): + """Apply inputs at the falling edge; the following rising edge samples them.""" + for k, v in kw.items(): + getattr(dut, k).value = v + await FallingEdge(dut.clk) + dut.wmask.value, dut.ld_en.value = 0, 0 + return int(dut.rdata.value) + + +@cocotb.test() +async def read_data_arrives_one_clock_after_the_address(dut): + await setup(dut) + await edge(dut, ld_en=1, ld_addr=3, ld_data=0xAABBCCDD) + await edge(dut, ld_en=1, ld_addr=4, ld_data=0x11223344) + assert await edge(dut, raddr=3) == 0xAABBCCDD + assert await edge(dut, raddr=4) == 0x11223344 + assert await edge(dut, raddr=4) == 0x11223344 # address unchanged -> data unchanged + + +@cocotb.test() +async def re_low_holds_the_output(dut): + await setup(dut) + await edge(dut, ld_en=1, ld_addr=1, ld_data=0x1111) + await edge(dut, ld_en=1, ld_addr=2, ld_data=0x2222) + assert await edge(dut, raddr=1) == 0x1111 + assert await edge(dut, re=0, raddr=2) == 0x1111 # held, not updated + assert await edge(dut, re=1, raddr=2) == 0x2222 + + +@cocotb.test() +async def byte_masked_writes(dut): + await setup(dut) + await edge(dut, ld_en=1, ld_addr=7, ld_data=0x44332211) + await edge(dut, waddr=7, wdata=0xAABBCCDD, wmask=0b0110) + assert await edge(dut, raddr=7) == 0x44BBCC11 + + +@cocotb.test() +async def same_edge_read_and_write(dut): + """The store-then-load case: read and write the same word on one edge.""" + await setup(dut) + await edge(dut, ld_en=1, ld_addr=9, ld_data=0x44332211) + got = await edge(dut, raddr=9, waddr=9, wdata=0xAABBCCDD, wmask=0b0101) + want_new, want_old = 0x44BB22DD, 0x44332211 + assert got == (want_new if READ_NEW else want_old), f"{got:#x}" + assert await edge(dut, raddr=9) == want_new # the write itself happened either way + # a write to a different word never disturbs the read + assert await edge(dut, raddr=9, waddr=10, wdata=0xFFFFFFFF, wmask=0xF) == want_new + + +@cocotb.test() +async def random_traffic_matches_a_model(dut): + await setup(dut) + rng = random.Random(42) + model = [0] * 16 + for i in range(16): + await edge(dut, ld_en=1, ld_addr=i, ld_data=0) + for _ in range(500): + ra, wa = rng.randrange(16), rng.randrange(16) + wmask, wdata = rng.randrange(16), rng.getrandbits(32) + old = list(model) + for b in range(4): + if wmask >> b & 1: + model[wa] = (model[wa] & ~(0xFF << 8 * b)) | (wdata & (0xFF << 8 * b)) + got = await edge(dut, raddr=ra, waddr=wa, wdata=wdata, wmask=wmask) + want = model[ra] if (READ_NEW and ra == wa) else old[ra] + assert got == want, f"read {ra} write {wa}/{wmask:#x}: {got:#x} != {want:#x}" diff --git a/sim/test_mmio.py b/sim/test_mmio.py new file mode 100644 index 0000000..141dff2 --- /dev/null +++ b/sim/test_mmio.py @@ -0,0 +1,62 @@ +"""MMIO decode vs the golden ISS's fault rules, for every region and access shape.""" + +import itertools + +import cocotb +from cocotb.triggers import Timer + +from consts import CONSOLE_ADDR, CYCLE_ADDR, DMEM_BASE, TOHOST_ADDR +from golden.iss import BusFault, Iss + +ADDRS = [0x0, 0x4, 0xFFC, 0x1000, DMEM_BASE - 4, DMEM_BASE, DMEM_BASE + 0xFFC, DMEM_BASE + 0x1000, + CONSOLE_ADDR - 4, CONSOLE_ADDR, TOHOST_ADDR, CYCLE_ADDR, CYCLE_ADDR + 4, 0x8000_0000, 0xFFFF_FFFC] + + +def iss_faults(kind, addr, mask): + iss = Iss() + try: + if kind == "load": + size, off = {1: ("lb", 0), 3: ("lh", 0), 0xF: ("lw", 0)}.get(mask, ("lb", 0)) + iss._load(size, addr, 0, 0) + else: + iss._store({1: "sb", 3: "sh", 0xF: "sw"}.get(mask, "sb"), addr, 0xA5, 0, 0) + except BusFault: + return True + return False + + +async def drive(dut, addr, re, we, mask, wdata=0x1234_5678, cycle=0xCAFE): + dut.word_addr.value, dut.re.value, dut.we.value = addr, re, we + dut.wmask.value, dut.wdata.value, dut.cycle.value = mask, wdata, cycle + await Timer(1, "ns") + + +@cocotb.test() +async def fault_rules_match_iss(dut): + for addr, mask in itertools.product(ADDRS, (1, 3, 0xF)): + await drive(dut, addr, 1, 0, mask) + assert bool(int(dut.fault.value)) == iss_faults("load", addr, mask), f"load {addr:#x}" + await drive(dut, addr, 0, 1, mask) + assert bool(int(dut.fault.value)) == iss_faults("store", addr, mask), f"store {addr:#x} mask={mask:#x}" + await drive(dut, addr, 0, 0, mask) + assert int(dut.fault.value) == 0, "no access, no fault" + + +@cocotb.test() +async def region_hits_and_register_effects(dut): + await drive(dut, DMEM_BASE + 8, 1, 0, 0xF) + assert int(dut.hit_dmem.value) == 1 and int(dut.console_we.value) == 0 + await drive(dut, DMEM_BASE + 0x1000, 1, 0, 0xF) + assert int(dut.hit_dmem.value) == 0 + await drive(dut, CONSOLE_ADDR, 0, 1, 0b0001, wdata=0x41) + assert int(dut.console_we.value) == 1 and int(dut.console_data.value) == 0x41 + await drive(dut, CONSOLE_ADDR, 0, 1, 0b0010, wdata=0x4100) # lane 1 only: nothing printed + assert int(dut.console_we.value) == 0 + await drive(dut, TOHOST_ADDR, 0, 1, 0xF, wdata=1) + assert int(dut.tohost_we.value) == 1 and int(dut.tohost_data.value) == 1 + await drive(dut, TOHOST_ADDR, 0, 1, 0b0001, wdata=1) + assert int(dut.tohost_we.value) == 0 and int(dut.fault.value) == 1 + await drive(dut, CYCLE_ADDR, 1, 0, 0xF, cycle=0xBEEF) + assert int(dut.mmio_rdata.value) == 0xBEEF and int(dut.fault.value) == 0 + await drive(dut, DMEM_BASE, 1, 0, 0xF, cycle=0xBEEF) + assert int(dut.mmio_rdata.value) == 0 diff --git a/sim/test_pipe_hazards.py b/sim/test_pipe_hazards.py new file mode 100644 index 0000000..dbb515a --- /dev/null +++ b/sim/test_pipe_hazards.py @@ -0,0 +1,63 @@ +"""Directed hazard patterns on the 5-stage core: PINNED cycle counts and the hazard debug flags. + +The expected "extra cycles" in golden/hazard_cases.py were derived on paper (not from the RTL) and are +checked against the independent model in the ISS-only tests; here the RTL must hit the same numbers in +both forwarding modes, and its dbg_stall / dbg_load_use / dbg_fwd_* pins must tell the same story. +""" + +import cocotb +from cocotb.triggers import ReadOnly, RisingEdge + +from golden.hazard_cases import CASES, DMEM +from golden.iss import Iss +from golden.pipemodel import is_redirect, stalls +from lockstep_lib import CORE, e, lockstep, reset_and_load + +assert CORE == "PIPE", "run through `bash sim/run.sh pipe`" + +LOAD_USE_ONCE = {"load->use dist 1", "load->store-data dist 1", "load->store-address dist 1", "csr->use dist 1", + "both operands late"} +# forwarding sources seen (1 = EX/MEM, 2 = MEM/WB) when forwarding is ON; everything else must be quiet +FWD_SEEN = {"alu->alu dist 1": {1}, "alu->alu dist 2": {2}, "load->use dist 1": {2}, "load->use dist 2": {2}, + "load->store-data dist 1": {2}, "load->store-address dist 1": {2}, "csr->use dist 1": {2}, + "both operands late": {2}} + + +def image(insns) -> bytes: + return b"".join(w.to_bytes(4, "little") for w in (*insns, e("ecall"))) + + +async def observe(dut, imem, forward): + await reset_and_load(dut, imem, DMEM, forward) + rows = [] + for _ in range(2000): + await RisingEdge(dut.clk) + await ReadOnly() + rows.append({k: int(getattr(dut, f"dbg_{k}").value) for k in ("stall", "load_use", "fwd_a", "fwd_b", "flush_ifid")} + | {"halted": int(dut.halted.value)}) + if rows[-1]["halted"]: + return rows + raise AssertionError("no halt") + + +@cocotb.parametrize(name=sorted(CASES), forward=[1, 0]) +async def test_pinned_cycle_counts(dut, name, forward): + insns, extra_on, extra_off = CASES[name] + iss, _, cycles = await lockstep(dut, image(insns), DMEM, expect_fault=False, forward=forward) + want = extra_on if forward else extra_off + assert cycles - (iss.instret + 4) == want, ( + f"{name} forward={forward}: {cycles - (iss.instret + 4)} extra cycles, expected {want}") + + +@cocotb.parametrize(name=sorted(CASES), forward=[1, 0]) +async def test_debug_flags(dut, name, forward): + insns, _, _ = CASES[name] + imem = image(insns) + trace = Iss(imem, DMEM).run(1000) + rows = await observe(dut, imem, forward) + + assert sum(r["stall"] for r in rows) == stalls(trace, bool(forward)), f"{name}: stall cycles" + assert sum(r["flush_ifid"] for r in rows) == sum(is_redirect(r) for r in trace), f"{name}: flushes" + assert sum(r["load_use"] for r in rows) == (1 if name in LOAD_USE_ONCE else 0), f"{name}: load-use pulses" + seen = {v for r in rows for v in (r["fwd_a"], r["fwd_b"]) if v} + assert seen == (FWD_SEEN.get(name, set()) if forward else set()), f"{name} forward={forward}: fwd sources {seen}" diff --git a/sim/test_pipe_lockstep.py b/sim/test_pipe_lockstep.py new file mode 100644 index 0000000..f22c771 --- /dev/null +++ b/sim/test_pipe_lockstep.py @@ -0,0 +1,208 @@ +"""5-stage core (CORE="PIPE") vs the golden ISS in lockstep, with forwarding ON and OFF. + +Everything runs in both modes (the cfg_forward_en pin is runtime): hazard-free programs, the official +riscv-tests images (back-to-back dependencies, load-use, store->load), the example programs, and +hazard-dense random programs. Cycle counts must equal the independent model golden/pipemodel.py. +Run with TINYCPU_CORE=PIPE and -GCORE="PIPE" (sim/run.sh pipe). RANDOM_SEEDS=N scales the random sweep. +""" + +import os + +import cocotb +from cocotb.triggers import ReadOnly, RisingEdge + +from golden.hazard_free import PROGRAMS, random_program +from golden.hazard_random import random_hazard_program +from golden.progbuild import Prog +from lockstep_lib import CORE, EXPECTED, IMEM_SIZE, e, images, lockstep, prog_bytes, reset_and_load, stem_path +from pathlib import Path + +SEEDS = int(os.environ.get("RANDOM_SEEDS", "200")) +MODES = [1, 0] # cfg_forward_en + +assert CORE == "PIPE", "run through `bash sim/run.sh pipe` (sets TINYCPU_CORE=PIPE and -GCORE=\"PIPE\")" + + +@cocotb.parametrize(name=sorted(PROGRAMS), forward=MODES) +async def test_hazard_free_program(dut, name, forward): + imem, dmem = PROGRAMS[name]().build() + iss, console, _ = await lockstep(dut, imem, dmem, expect_fault=False, forward=forward) + assert iss.passed + if name == "console_and_csr": + assert console == b"Hi, pipe!\n!" + + +@cocotb.parametrize(seed=list(range(60)), forward=MODES) +async def test_random_hazard_free(dut, seed, forward): + imem, dmem = random_program(seed).build() + iss, _, _ = await lockstep(dut, imem, dmem, expect_fault=False, forward=forward) + assert iss.passed + + +@cocotb.parametrize(seed=list(range(SEEDS)), forward=MODES) +async def test_random_hazard_dense(dut, seed, forward): + """Dense code over 5 registers: RAW at every distance, load-use, store->load, jumps, CSR reads.""" + imem, dmem = random_hazard_program(seed).build() + iss, _, _ = await lockstep(dut, imem, dmem, expect_fault=False, forward=forward) + assert iss.passed + + +# --------------------------------------------------------------------------- official tests + programs +NAMES = images() +if NAMES: + + @cocotb.parametrize(name=NAMES, forward=MODES) + async def test_image(dut, name, forward): + """rv32ui-p-* and sim/programs/*.S, unmodified: only possible now that hazards are handled.""" + stem = stem_path(name) + imem, dmem = Path(str(stem) + ".imem.bin").read_bytes(), Path(str(stem) + ".dmem.bin").read_bytes() + iss, console, _ = await lockstep(dut, imem, dmem, expect_fault=False, forward=forward) + assert iss.passed, f"{name}: failed test case #{(iss.tohost or 0) >> 1}" + if not name.startswith("rv32ui"): + assert console.decode() == EXPECTED[name]["console"] + assert iss.instret == EXPECTED[name]["instret"] + + +# --------------------------------------------------------------------------- halt and faults +@cocotb.test() +async def ecall_ebreak_halt(dut): + for insn in (e("ecall"), e("ebreak")): + p = Prog().addi(1, 0, 7) + p.raw(insn).addi(1, 0, 9) + for forward in MODES: + iss, _, _ = await lockstep(dut, p.build()[0], expect_fault=False, forward=forward) + assert iss.regs[1] == 7 and iss.tohost is None + + +def fault_cases(): + def build(*items, tail=()): + p = Prog() + for it in items: + it(p) + return p.build()[0] + + return { + "illegal instruction": build(lambda p: p.addi(1, 0, 5), lambda p: p.raw(0x00000000)), + "csrrs with rs1 != x0": build(lambda p: p.raw(e("csrrs", 1, 1, csr=0xC00))), + "unknown csr": build(lambda p: p.raw(e("csrrs", 1, 0, csr=0x300))), + "load from IMEM": build(lambda p: p.lw(1, 0, 0)), + "store to IMEM": build(lambda p: p.sw(0, 0, 0)), + "unmapped load": build(lambda p: p.lui(1, 0x80000), lambda p: p.lw(2, 1, 0)), + "tohost byte store": build(lambda p: p.lui(1, 0x20), lambda p: p.sb(0, 1, 4)), + "store to CYCLE": build(lambda p: p.lui(1, 0x20), lambda p: p.sw(0, 1, 8)), + "misaligned jal": build(lambda p: p.raw(e("jal", 0, imm=6))), + "misaligned taken branch": build(lambda p: p.raw(e("beq", 0, 0, 0, imm=2))), + "jalr to odd word": build(lambda p: p.addi(1, 0, 6), lambda p: p.jalr(0, 1, 0)), + "jump past end of IMEM": build(lambda p: p.lui(1, 1), lambda p: p.jalr(0, 1, 0)), + "fall off end of IMEM": prog_bytes(e("jal", 0, imm=IMEM_SIZE - 4)), + } + + +@cocotb.test() +async def faults_stop_the_core_without_retiring(dut): + for forward in MODES: + for label, imem in fault_cases().items(): + try: + await lockstep(dut, imem, expect_fault=True, forward=forward) + except AssertionError as exc: + raise AssertionError(f"[{label}, forward={forward}] {exc}") from exc + + +def setup_console(p): + """x1 = CONSOLE, x2 = 'A', x3 = 'B', then 3 NOPs so the following dense code has no hazards.""" + p.li(1, 0x20000) + p.li(2, ord("A")) + p.li(3, ord("B")) + p.nop(3) + + +@cocotb.test() +async def younger_instructions_have_no_effect_after_a_fault(dut): + """Dense (pad=0) code: 'A' prints, the faulting instruction stops the core, and the stores right + behind it (already in EX/MEM when it faults) must NOT print.""" + for label, faulting in { + "illegal": lambda p: p.raw(0x00000000), + "bus fault": lambda p: p.lw(9, 0, 0), + "misaligned jal": lambda p: p.raw(e("jal", 0, imm=6)), + }.items(): + p = Prog(pad=0) + setup_console(p) + p.sb(2, 1, 0) + faulting(p) + p.sb(3, 1, 0).sb(3, 1, 0).sb(3, 1, 0).nop(3) + p.exit() + for forward in MODES: + try: + iss, console, _ = await lockstep(dut, p.build()[0], expect_fault=True, forward=forward) + except AssertionError as exc: + raise AssertionError(f"[{label}, forward={forward}] {exc}") from exc + assert console == b"A", f"[{label}, forward={forward}] younger stores leaked: {console!r}" + + +@cocotb.test() +async def younger_instructions_have_no_effect_after_halt(dut): + for label, halting in { + "tohost store": lambda p: (p.li(5, 0x20004), p.li(6, 1), p.nop(2), p.sw(6, 5, 0)), + "ecall": lambda p: p.raw(e("ecall")), + }.items(): + p = Prog(pad=0) + setup_console(p) + p.sb(2, 1, 0) + halting(p) + p.sb(3, 1, 0).sb(3, 1, 0).sb(3, 1, 0) + for forward in MODES: + iss, console, _ = await lockstep(dut, p.build()[0], expect_fault=False, forward=forward) + assert console == b"A", f"[{label}, forward={forward}] instructions after the halt leaked: {console!r}" + + +# --------------------------------------------------------------------------- pipeline behaviour +async def trace_run(dut, imem, dmem): + """Run to halt, recording every cycle's stage occupancy (after each clock edge).""" + await reset_and_load(dut, imem, dmem) + rows = [] + for _ in range(100_000): + await RisingEdge(dut.clk) + await ReadOnly() + valid = int(dut.dbg_stage_valid.value) + pcs = int(dut.dbg_stage_pc.value) + rows.append({"valid": valid, "pc": [(pcs >> (32 * s)) & 0xFFFFFFFF for s in range(5)], + "flush": int(dut.dbg_flush_ifid.value), "flush2": int(dut.dbg_flush_idex.value), + "retire": int(dut.rvfi_valid.value), "halted": int(dut.halted.value)}) + if rows[-1]["halted"]: + return rows + raise AssertionError("no halt") + + +@cocotb.test() +async def five_instructions_overlap_and_flushes_match_redirects(dut): + p = Prog(pad=0) + p.li(1, 3).li(2, 5).nop(3) + p.add(3, 1, 2).addi(4, 1, 1).sub(5, 2, 1).xor(6, 1, 2).nop(3) + p.jal(0, "skip").addi(7, 0, 99).addi(7, 0, 98).label("skip").nop(3) + p.exit() + imem, dmem = p.build() + rows = await trace_run(dut, imem, dmem) + + assert any(r["valid"] == 0b11111 and len(set(r["pc"])) == 5 for r in rows), "never had 5 instructions in flight" + # every flush flag comes with its twin and exactly one jal redirected + assert all(r["flush"] == r["flush2"] for r in rows) + assert sum(r["flush"] for r in rows) == 1 + + # the classic pipeline diagram, for the log + first = {} + lines = ["cycle IF ID EX MEM WB"] + for n, r in enumerate(rows[:24], 1): + cells = [] + for s in range(5): + if r["valid"] >> s & 1: + cells.append(f"{r['pc'][s]:#06x} ") + else: + cells.append(" -- ") + lines.append(f"{n:5d} " + " ".join(cells) + (" <- flush" if r["flush"] else "")) + dut._log.info("\n" + "\n".join(lines)) + + # in-order: a given pc advances one stage per cycle when it is not squashed + for n in range(len(rows) - 1): + for s in range(4): + if rows[n]["valid"] >> s & 1 and rows[n + 1]["valid"] >> (s + 1) & 1 and not rows[n]["flush"]: + assert rows[n + 1]["pc"][s + 1] == rows[n]["pc"][s], f"cycle {n + 1}: stage {s}->{s + 1} pc mismatch" diff --git a/sim/test_regfile.py b/sim/test_regfile.py new file mode 100644 index 0000000..7fc05d3 --- /dev/null +++ b/sim/test_regfile.py @@ -0,0 +1,73 @@ +import random + +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import FallingEdge, ReadOnly, RisingEdge, Timer + + +async def setup(dut): + cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start()) + dut.we.value, dut.waddr.value, dut.wdata.value = 0, 0, 0 + dut.raddr1.value = dut.raddr2.value = 0 + dut.rst_n.value = 0 + await Timer(12, "ns") + dut.rst_n.value = 1 + await FallingEdge(dut.clk) + + +async def write(dut, addr, val): + dut.we.value, dut.waddr.value, dut.wdata.value = 1, addr, val + await FallingEdge(dut.clk) # the rising edge in between performs the write + dut.we.value = 0 + + +async def read(dut, a1, a2=0): + dut.raddr1.value, dut.raddr2.value = a1, a2 + await Timer(1, "ns") + return int(dut.rdata1.value), int(dut.rdata2.value) + + +@cocotb.test() +async def reset_clears_everything(dut): + await setup(dut) + for i in range(32): + assert (await read(dut, i, 31 - i)) == (0, 0) + assert int(dut.dbg_regs.value) == 0 + + +@cocotb.test() +async def write_read_all_registers_and_x0_is_zero(dut): + await setup(dut) + rng = random.Random(42) + model = [0] * 32 + for _ in range(600): + a, v = rng.randrange(32), rng.getrandbits(32) + await write(dut, a, v) + if a: + model[a] = v + r1, r2 = rng.randrange(32), rng.randrange(32) + assert (await read(dut, r1, r2)) == (model[r1], model[r2]) + assert model[0] == 0 + dbg = int(dut.dbg_regs.value) + assert [(dbg >> (32 * i)) & 0xFFFFFFFF for i in range(32)] == model + + +@cocotb.test() +async def write_first_bypass(dut): + await setup(dut) + await write(dut, 5, 0x1111) + dut.we.value, dut.waddr.value, dut.wdata.value = 1, 5, 0x2222 + assert (await read(dut, 5, 6)) == (0x2222, 0) # same-cycle read sees the new value + dut.waddr.value = 0 + assert (await read(dut, 0, 5)) == (0, 0x1111) # x0 never bypasses; x5 not yet overwritten + dut.we.value = 0 + await FallingEdge(dut.clk) + assert (await read(dut, 5, 5)) == (0x1111, 0x1111) + + +@cocotb.test() +async def we_low_does_not_write(dut): + await setup(dut) + dut.we.value, dut.waddr.value, dut.wdata.value = 0, 9, 0xDEAD + await FallingEdge(dut.clk) + assert (await read(dut, 9)) == (0, 0) diff --git a/sim/test_regfile_nobypass.py b/sim/test_regfile_nobypass.py new file mode 100644 index 0000000..2a35eb5 --- /dev/null +++ b/sim/test_regfile_nobypass.py @@ -0,0 +1,17 @@ +"""regfile with BYPASS=0 (single-cycle core): a read never sees the write happening this cycle.""" + +import cocotb +from cocotb.triggers import FallingEdge + +from test_regfile import read, setup, write + + +@cocotb.test() +async def read_returns_old_value_during_a_write(dut): + await setup(dut) + await write(dut, 5, 0x1111) + dut.we.value, dut.waddr.value, dut.wdata.value = 1, 5, 0x2222 + assert (await read(dut, 5, 5)) == (0x1111, 0x1111) # no bypass + await FallingEdge(dut.clk) # the write lands at the rising edge + dut.we.value = 0 + assert (await read(dut, 5, 5)) == (0x2222, 0x2222) diff --git a/sw/.gitkeep b/sw/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/sw/Makefile b/sw/Makefile new file mode 100644 index 0000000..67a9086 --- /dev/null +++ b/sw/Makefile @@ -0,0 +1,51 @@ +# Builds the official rv32ui riscv-tests and our asm programs for the TinyCPU memory map. +# Outputs per program NAME under build//: +# NAME.elf NAME.imem.bin (from 0x0) NAME.dmem.bin (from 0x10000) NAME.dis +# +# make riscv-tests | programs | all | clean +# Needs riscv-none-elf-gcc on PATH (bash ../.tools/riscv-gcc.sh, then source ../scripts/env.sh). + +RISCV ?= riscv-none-elf +CC := $(RISCV)-gcc +OBJCOPY := $(RISCV)-objcopy +OBJDUMP := $(RISCV)-objdump + +ARCH := -march=rv32i_zicsr_zifencei -mabi=ilp32 +CFLAGS := $(ARCH) -static -nostdlib -nostartfiles -ffreestanding \ + -I env/tinycpu -I riscv-tests/isa/macros/scalar -I ../sim/programs +LDFLAGS := -T link.ld -Wl,--no-warn-rwx-segments + +BUILD := build + +# fence_i needs self-modifying code (Harvard: unsupported); ma_data needs misaligned access. +EXCLUDE_TESTS := fence_i ma_data +TEST_NAMES := $(filter-out $(EXCLUDE_TESTS),$(basename $(notdir $(wildcard riscv-tests/isa/rv32ui/*.S)))) +PROG_NAMES := $(basename $(notdir $(wildcard ../sim/programs/*.S))) + +TEST_BINS := $(foreach t,$(TEST_NAMES),$(BUILD)/riscv-tests/rv32ui-p-$(t).dmem.bin) +PROG_BINS := $(foreach p,$(PROG_NAMES),$(BUILD)/programs/$(p).dmem.bin) + +.PHONY: all riscv-tests programs clean +.SECONDARY: # keep .elf/.imem.bin: only the .dmem.bin is a named goal +all: riscv-tests programs +riscv-tests: $(TEST_BINS) +programs: $(PROG_BINS) + +$(BUILD)/riscv-tests/rv32ui-p-%.elf: riscv-tests/isa/rv32ui/%.S link.ld env/tinycpu/riscv_test.h + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ + +$(BUILD)/programs/%.elf: ../sim/programs/%.S ../sim/programs/common.h link.ld + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ + +%.imem.bin: %.elf + $(OBJCOPY) -O binary -j .text $< $@ + +# The imem rule runs first (dmem depends on it) so a single make target yields both images. +%.dmem.bin: %.elf %.imem.bin + $(OBJCOPY) -O binary -j .data $< $@ + $(OBJDUMP) -d -M no-aliases $< > $*.dis + +clean: + rm -rf $(BUILD) diff --git a/sw/crt0.S b/sw/crt0.S new file mode 100644 index 0000000..fe9a194 --- /dev/null +++ b/sw/crt0.S @@ -0,0 +1,17 @@ +/* Startup for C programs (not used by the riscv-tests or the asm programs in sim/programs). */ + .section .text.init + .globl _start +_start: + la sp, _stack_top + la t0, __bss_start + la t1, __bss_end +1: bgeu t0, t1, 2f + sw zero, 0(t0) + addi t0, t0, 4 + j 1b +2: call main + slli a0, a0, 1 /* exit code: 1 = pass (main returned 0), else (code<<1)|1 */ + ori a0, a0, 1 + lui t0, 0x20 + sw a0, 4(t0) /* TOHOST */ +3: j 3b diff --git a/sw/env/tinycpu/riscv_test.h b/sw/env/tinycpu/riscv_test.h new file mode 100644 index 0000000..7baf01c --- /dev/null +++ b/sw/env/tinycpu/riscv_test.h @@ -0,0 +1,51 @@ +// Replacement for riscv-tests/env/p/riscv_test.h targeting the TinyCPU memory map. +// No privileged spec: tests start at _start (address 0) and report through TOHOST. +// PASS -> tohost = 1; FAIL -> tohost = (TESTNUM << 1) | 1 (TESTNUM > 0, so never 1) +#ifndef _ENV_TINYCPU_H +#define _ENV_TINYCPU_H + +#define TOHOST_ADDR_HI 0x20 /* lui t1, 0x20 -> 0x0002_0000; TOHOST is at +4 */ + +#define RVTEST_RV32U +#define RVTEST_RV64U RVTEST_RV32U /* the rv32ui wrappers #include rv64ui sources */ +#define RVTEST_RV32M +#define TESTNUM gp + +#define RVTEST_CODE_BEGIN \ + .section .text.init; \ + .align 2; \ + .globl _start; \ +_start: \ + li TESTNUM, 0; + +#define RVTEST_CODE_END \ + unimp; + +#define RVTEST_PASS \ + li t0, 1; \ + lui t1, TOHOST_ADDR_HI; \ + sw t0, 4(t1); \ +1: j 1b; + +#define RVTEST_FAIL \ + slli t0, TESTNUM, 1; \ + ori t0, t0, 1; \ + lui t1, TOHOST_ADDR_HI; \ + sw t0, 4(t1); \ +1: j 1b; + +#define EXTRA_DATA + +#define RVTEST_DATA_BEGIN \ + EXTRA_DATA \ + .data; \ + .align 4; \ + .global begin_signature; \ +begin_signature: + +#define RVTEST_DATA_END \ + .align 4; \ + .global end_signature; \ +end_signature: + +#endif diff --git a/sw/link.ld b/sw/link.ld new file mode 100644 index 0000000..82fe8ef --- /dev/null +++ b/sw/link.ld @@ -0,0 +1,38 @@ +/* TinyCPU memory map (docs/PLAN.md section 2.1). Harvard: the data port cannot read IMEM, + so .rodata lives in DMEM next to .data and ships in the dmem image. */ +OUTPUT_ARCH(riscv) +ENTRY(_start) + +MEMORY +{ + IMEM (rx) : ORIGIN = 0x00000000, LENGTH = 4K + DMEM (rw) : ORIGIN = 0x00010000, LENGTH = 4K +} + +SECTIONS +{ + .text : { + *(.text.init) + *(.text .text.*) + } > IMEM + + .data : { + *(.rodata .rodata.*) + *(.data .data.*) + *(.sdata .sdata.*) + . = ALIGN(4); + } > DMEM + + .bss (NOLOAD) : { + __bss_start = .; + *(.sbss .sbss.*) + *(.bss .bss.*) + *(COMMON) + . = ALIGN(4); + __bss_end = .; + } > DMEM + + _stack_top = ORIGIN(DMEM) + LENGTH(DMEM); + + /DISCARD/ : { *(.comment) *(.note*) *(.eh_frame*) } +} diff --git a/sw/riscv-tests b/sw/riscv-tests new file mode 160000 index 0000000..3df703c --- /dev/null +++ b/sw/riscv-tests @@ -0,0 +1 @@ +Subproject commit 3df703cd52d5f0d8224e3c50bf03c6cb70efff98 diff --git a/wasm/README.md b/wasm/README.md index 7e206b9..14eba38 100644 --- a/wasm/README.md +++ b/wasm/README.md @@ -1,8 +1,19 @@ # wasm/ -`spike/` is the Phase 0 proof that Verilator + Emscripten work here, including two Verilated -models (distinct `--prefix`) in one module. Phase 5 adds `harness.cpp`, `bindings.cpp`, `build.sh`. +`harness.cpp` and `bindings.cpp` build both microarchitectures of `tiny_cpu_top` into one WebAssembly module +(`web/public/tiny_cpu.{mjs,wasm}`). The JavaScript contract is `docs/STATE_SCHEMA.md`. ```bash -bash wasm/spike/build.sh && node wasm/spike/test.mjs +source ../scripts/env.sh # Verilator 5.036 and Emscripten (see CLAUDE.md for the one-time bootstrap) +bash wasm/build.sh # two Verilations (CORE=SINGLE, CORE=PIPE) + em++ + size budget (400 KB gzipped) +node wasm/test.mjs # every fixture on both cores, forwarding on and off: console, regs, dmem, digest, cycles ``` + +- `test/fixtures.json` is generated from the golden ISS: `cd sim && python -m golden.wasm_fixtures`. + `sim/golden/test_wasm_fixtures.py` fails if it is stale. +- `build.sh` keeps Emscripten's temporaries under `wasm/build/` so a full system disk can't break it. +- `spike/` is the Phase 0 toolchain check (one model per prefix, two counters); kept as a regression of the setup. +- `emscripten_stubs.cpp` supplies the pthread affinity calls that `verilated_threads.cpp` references. + +Mutation checks (see CONTEXT.md): dropping a console byte, a cycle off by one, ignoring the forwarding pin and +reversing the shadow-memory byte order each fail `wasm/test.mjs`. diff --git a/wasm/bindings.cpp b/wasm/bindings.cpp new file mode 100644 index 0000000..82411fa --- /dev/null +++ b/wasm/bindings.cpp @@ -0,0 +1,23 @@ +// Emscripten embind glue. Build with wasm/build.sh; the JS API is documented in docs/STATE_SCHEMA.md. +#include "harness.cpp" + +#include + +EMSCRIPTEN_BINDINGS(tiny_cpu) { + emscripten::class_("TinyCpuSim") + .constructor<>() + .function("reset", &TinyCpuSim::reset) + .function("setForwarding", &TinyCpuSim::setForwarding) + .function("loadImem", &TinyCpuSim::loadImem) + .function("loadDmem", &TinyCpuSim::loadDmem) + .function("start", &TinyCpuSim::start) + .function("step", &TinyCpuSim::step) + .function("run", &TinyCpuSim::run) + .function("runFast", &TinyCpuSim::runFast) + .function("getRegs", &TinyCpuSim::getRegs) + .function("readDmem", &TinyCpuSim::readDmem) + .function("getConsole", &TinyCpuSim::getConsole) + .function("getCycleCount", &TinyCpuSim::getCycleCount) + .function("isHalted", &TinyCpuSim::isHalted) + .function("isFault", &TinyCpuSim::isFault); +} diff --git a/wasm/build.sh b/wasm/build.sh new file mode 100755 index 0000000..0f98a3c --- /dev/null +++ b/wasm/build.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# Phase 5: Verilate tiny_cpu_top twice - CORE="SINGLE" and CORE="PIPE", distinct --prefix - and link both +# into one WebAssembly module. Outputs web/public/tiny_cpu.mjs + tiny_cpu.wasm and checks the size budget. +# +# Prerequisites (run `source scripts/env.sh` first): Verilator >= 5.036 on PATH, Emscripten (em++). +# Emscripten's temporary files go under wasm/build, not /tmp, so a full system disk cannot break the build. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +BUILD="$SCRIPT_DIR/build" +OUT_DIR="$PROJECT_ROOT/web/public" +SIZE_BUDGET_KB=400 # gzipped .wasm, docs/PLAN.md section 7 + +export TMPDIR="$BUILD/tmp" +mkdir -p "$TMPDIR" "$OUT_DIR" + +VROOT="$(verilator --getenv VERILATOR_ROOT)" +echo "[info] VERILATOR_ROOT = $VROOT" + +RTL="" +for f in tiny_cpu_pkg alu imm_gen branch_unit lsu decoder regfile csr_counters mmio mem_comb mem_sync \ + forward_unit hazard_unit core_single core_pipe tiny_cpu_top; do + RTL+=" $PROJECT_ROOT/rtl/$f.sv" +done + +verilate() { # verilate + rm -rf "$3" + mkdir -p "$3" + # shellcheck disable=SC2086 + verilator --cc $RTL --top-module tiny_cpu_top --prefix "$2" -G"CORE=\"$1\"" \ + --Mdir "$3" -Wall -Wno-DECLFILENAME +} + +echo "[1/3] Verilating tiny_cpu_top for CORE=SINGLE and CORE=PIPE ..." +verilate SINGLE Vcpu_single "$BUILD/obj_single" +verilate PIPE Vcpu_pipe "$BUILD/obj_pipe" + +echo "[2/3] Compiling and linking WASM with Emscripten ..." +em++ -O2 -std=c++17 -DVL_IGNORE_UNKNOWN_ARCH \ + -I "$BUILD/obj_single" -I "$BUILD/obj_pipe" -I "$SCRIPT_DIR" -I "$VROOT/include" \ + "$SCRIPT_DIR/bindings.cpp" \ + "$BUILD"/obj_single/Vcpu_single*.cpp \ + "$BUILD"/obj_pipe/Vcpu_pipe*.cpp \ + "$VROOT/include/verilated.cpp" \ + "$VROOT/include/verilated_threads.cpp" \ + "$SCRIPT_DIR/emscripten_stubs.cpp" \ + -lembind \ + -s MODULARIZE=1 -s EXPORT_ES6=1 -s ALLOW_MEMORY_GROWTH=1 \ + -s EXPORT_NAME=createTinyCpu \ + -o "$OUT_DIR/tiny_cpu.mjs" + +echo "[3/3] Checking the size budget ..." +WASM="$OUT_DIR/tiny_cpu.wasm" +RAW=$(wc -c < "$WASM") +GZ=$(gzip -9 -c "$WASM" | wc -c) +echo " tiny_cpu.wasm: $RAW bytes raw, $GZ bytes gzipped (budget ${SIZE_BUDGET_KB} KB gzipped)" +if [ "$GZ" -gt $((SIZE_BUDGET_KB * 1024)) ]; then + echo "ERROR: gzipped WASM exceeds the ${SIZE_BUDGET_KB} KB budget" >&2 + exit 1 +fi +echo "Done: $OUT_DIR/tiny_cpu.mjs + tiny_cpu.wasm" diff --git a/wasm/harness.cpp b/wasm/harness.cpp new file mode 100644 index 0000000..42caeca --- /dev/null +++ b/wasm/harness.cpp @@ -0,0 +1,386 @@ +// Phase 5: both microarchitectures of tiny_cpu_top in one WebAssembly module. +// +// tiny_cpu_top is Verilated twice - CORE="SINGLE" and CORE="PIPE", distinct --prefix - and each +// model is wrapped by a Model. TinyCpuSim owns both and selects one with reset(core). +// Program memory is written into both, so either core can be selected after loading. +// +// Nothing here re-implements the design: every value returned comes from an output port of the +// Verilated model. The one exception is data memory. The RTL does not expose it, so the harness +// keeps a shadow copy updated from the retire records (rvfi_mem_*), which the RTL drives with the +// exact lane-positioned store data. Loads never change memory, so the shadow is exact. +#include "Vcpu_pipe.h" +#include "Vcpu_single.h" +#include "verilated.h" + +#include + +#include +#include +#include +#include +#include + +using emscripten::val; + +namespace { + +constexpr uint32_t IMEM_WORDS = 1024; +constexpr uint32_t DMEM_WORDS = 1024; +constexpr uint32_t DMEM_BASE = 0x0001'0000; +constexpr uint32_t DMEM_BYTES = DMEM_WORDS * 4; +constexpr uint32_t CONSOLE_ADDR = 0x0002'0000; + +// Everything one clock edge exposes, copied out of the model. +struct Snap { + bool halted = false, fault = false; + bool rvfi_valid = false, rvfi_halt = false, rvfi_cycle_dep = false, rvfi_misaligned = false; + uint32_t order = 0, pc = 0, insn = 0, rd_wdata = 0, pc_wdata = 0; + uint32_t mem_addr = 0, mem_rdata = 0, mem_wdata = 0; + uint8_t rd_addr = 0, mem_rmask = 0, mem_wmask = 0; + uint32_t dbg_pc = 0, instret = 0, cycle = 0; + uint8_t stage_valid = 0; + std::array stage_pc{}, stage_insn{}; + bool stall = false, load_use = false, flush_ifid = false, flush_idex = false; + uint8_t fwd_a = 0, fwd_b = 0; + std::array regs{}; + bool console_valid = false; + uint8_t console_data = 0; + bool tohost_valid = false; + uint32_t tohost_data = 0; +}; + +class ModelIface { +public: + virtual ~ModelIface() = default; + virtual void setForwarding(bool on) = 0; + virtual void assertReset() = 0; // async reset held; the clock may still run + virtual void releaseReset() = 0; // rst_n high; the next edge executes instruction 0 + virtual void writeWord(bool dmem, uint32_t index, uint32_t data) = 0; // requires reset held + virtual void tick() = 0; // one rising clock edge + virtual Snap snapshot() const = 0; + virtual void clearRun() = 0; // console and shadow tohost, for a new run + virtual std::vector readDmem(uint32_t offset, uint32_t len) const = 0; + virtual const std::string& console() const = 0; +}; + +template +class Model final : public ModelIface { +public: + Model() : ctx_(new VerilatedContext), top_(new Top(ctx_, "TOP")), dmem_(DMEM_BYTES, 0) { + top_->clk = 0; + top_->rst_n = 0; + top_->cfg_forward_en = 1; + top_->load_en = 0; + top_->load_sel = 0; + top_->load_addr = 0; + top_->load_data = 0; + top_->eval(); + } + + ~Model() override { + top_->final(); + delete top_; + delete ctx_; + } + + void setForwarding(bool on) override { top_->cfg_forward_en = on ? 1 : 0; } + + void assertReset() override { + top_->load_en = 0; + top_->rst_n = 0; + top_->clk = 0; + top_->eval(); + } + + void releaseReset() override { + top_->rst_n = 1; + top_->clk = 0; + top_->eval(); + } + + void writeWord(bool dmem, uint32_t index, uint32_t data) override { + top_->load_en = 1; + top_->load_sel = dmem ? 1 : 0; + top_->load_addr = index; + top_->load_data = data; + tick(); + top_->load_en = 0; + if (dmem) { + for (uint32_t b = 0; b < 4; ++b) dmem_[index * 4 + b] = (data >> (8 * b)) & 0xFF; + } + } + + void tick() override { + top_->clk = 0; + top_->eval(); + top_->clk = 1; + top_->eval(); + if (top_->console_valid) console_.push_back(static_cast(top_->console_data)); + if (top_->rvfi_valid && top_->rvfi_mem_wmask && top_->rvfi_mem_addr >= DMEM_BASE && + top_->rvfi_mem_addr < DMEM_BASE + DMEM_BYTES) { + const uint32_t off = top_->rvfi_mem_addr - DMEM_BASE; + for (uint32_t b = 0; b < 4; ++b) { + if (top_->rvfi_mem_wmask >> b & 1) dmem_[off + b] = (top_->rvfi_mem_wdata >> (8 * b)) & 0xFF; + } + } + } + + Snap snapshot() const override { + Snap s; + s.halted = top_->halted; + s.fault = top_->fault; + s.rvfi_valid = top_->rvfi_valid; + s.rvfi_halt = top_->rvfi_halt; + s.rvfi_cycle_dep = top_->rvfi_cycle_dep; + s.rvfi_misaligned = top_->rvfi_misaligned; + s.order = top_->rvfi_order; + s.pc = top_->rvfi_pc; + s.insn = top_->rvfi_insn; + s.rd_addr = top_->rvfi_rd_addr; + s.rd_wdata = top_->rvfi_rd_wdata; + s.pc_wdata = top_->rvfi_pc_wdata; + s.mem_addr = top_->rvfi_mem_addr; + s.mem_rmask = top_->rvfi_mem_rmask; + s.mem_wmask = top_->rvfi_mem_wmask; + s.mem_rdata = top_->rvfi_mem_rdata; + s.mem_wdata = top_->rvfi_mem_wdata; + s.dbg_pc = top_->dbg_pc; + s.instret = top_->dbg_instret; + s.cycle = top_->dbg_cycle; + s.stage_valid = top_->dbg_stage_valid; + for (uint32_t i = 0; i < 5; ++i) { + s.stage_pc[i] = top_->dbg_stage_pc[i]; + s.stage_insn[i] = top_->dbg_stage_insn[i]; + } + s.stall = top_->dbg_stall; + s.load_use = top_->dbg_load_use; + s.flush_ifid = top_->dbg_flush_ifid; + s.flush_idex = top_->dbg_flush_idex; + s.fwd_a = top_->dbg_fwd_a; + s.fwd_b = top_->dbg_fwd_b; + for (uint32_t i = 0; i < 32; ++i) s.regs[i] = top_->dbg_regs[i]; + s.console_valid = top_->console_valid; + s.console_data = top_->console_data; + s.tohost_valid = top_->tohost_valid; + s.tohost_data = top_->tohost_data; + return s; + } + + void clearRun() override { console_.clear(); } + + std::vector readDmem(uint32_t offset, uint32_t len) const override { + std::vector out; + for (uint32_t i = 0; i < len && offset + i < DMEM_BYTES; ++i) out.push_back(dmem_[offset + i]); + return out; + } + + const std::string& console() const override { return console_; } + +private: + VerilatedContext* ctx_; + Top* top_; + std::vector dmem_; + std::string console_; +}; + +// Length of a JS array-like (plain array or typed array). +uint32_t lengthOf(const val& arr) { return arr["length"].as(); } + +} // namespace + +class TinyCpuSim { +public: + TinyCpuSim() + : single_(std::make_unique>()), pipe_(std::make_unique>()) { + active_ = single_.get(); + } + + // Selects the core and asserts reset on both models. Returns false for an unknown core name. + bool reset(const std::string& core) { + if (core == "SINGLE") { + active_ = single_.get(); + } else if (core == "PIPE") { + active_ = pipe_.get(); + } else { + return false; + } + single_->assertReset(); + pipe_->assertReset(); + for (ModelIface* m : {static_cast(single_.get()), static_cast(pipe_.get())}) { + m->setForwarding(forward_); + m->clearRun(); + } + cycles_ = 0; + started_ = false; + return true; + } + + // Takes effect at the next reset(). Forwarding only matters for PIPE; SINGLE ignores it. + void setForwarding(bool on) { forward_ = on; } + + // Loads IMEM from a JS array of 32-bit words. Both models are held in reset while loading. + bool loadImem(const val& words) { + const uint32_t n = lengthOf(words); + if (n > IMEM_WORDS) return false; + single_->assertReset(); + pipe_->assertReset(); + for (uint32_t i = 0; i < IMEM_WORDS; ++i) { + const uint32_t w = i < n ? words[i].as() : 0; + single_->writeWord(false, i, w); + pipe_->writeWord(false, i, w); + } + return true; + } + + // Loads DMEM from a JS array of bytes (little-endian words), padded with zeros. + bool loadDmem(const val& bytes) { + const uint32_t n = lengthOf(bytes); + if (n > DMEM_BYTES) return false; + single_->assertReset(); + pipe_->assertReset(); + for (uint32_t i = 0; i < DMEM_WORDS; ++i) { + uint32_t w = 0; + for (uint32_t b = 0; b < 4; ++b) { + const uint32_t idx = i * 4 + b; + if (idx < n) w |= static_cast(bytes[idx].as()) << (8 * b); + } + single_->writeWord(true, i, w); + pipe_->writeWord(true, i, w); + } + return true; + } + + // Releases reset on the selected core. Cycle counting starts here. + void start() { + active_->releaseReset(); + cycles_ = 0; + started_ = true; + } + + // One clock edge. Returns the CycleState for that edge (see docs/STATE_SCHEMA.md). + val step() { + if (!started_) return val::null(); + if (!finished()) { + active_->tick(); + ++cycles_; + } + return toState(active_->snapshot()); + } + + // Steps until halted, faulted or maxCycles, returning every CycleState (the last is the halt). + val run(uint32_t maxCycles) { + val out = val::array(); + for (uint32_t i = 0; i < maxCycles && !finished(); ++i) { + out.call("push", step()); + } + return out; + } + + // Steps until halted, faulted or maxCycles without building per-cycle states. + val runFast(uint32_t maxCycles) { + if (started_) { + for (uint32_t i = 0; i < maxCycles && !finished(); ++i) { + active_->tick(); + ++cycles_; + } + } + const Snap s = active_->snapshot(); + val out = val::object(); + out.set("cycles", cycles_); + out.set("instret", s.instret); + out.set("halted", s.halted); + out.set("fault", s.fault); + out.set("tohost", s.tohost_valid ? val(s.tohost_data) : val::null()); + out.set("console", active_->console()); + return out; + } + + val getRegs() const { + const Snap s = active_->snapshot(); + val out = val::array(); + for (uint32_t i = 0; i < 32; ++i) out.call("push", s.regs[i]); + return out; + } + + // Bytes of data memory at an absolute address (DMEM_BASE-relative), from the shadow copy. + val readDmem(uint32_t addr, uint32_t len) const { + val out = val::array(); + if (addr < DMEM_BASE) return out; + for (uint8_t b : active_->readDmem(addr - DMEM_BASE, len)) out.call("push", static_cast(b)); + return out; + } + + std::string getConsole() const { return active_->console(); } + uint32_t getCycleCount() const { return cycles_; } + bool isHalted() const { return active_->snapshot().halted; } + bool isFault() const { return active_->snapshot().fault; } + +private: + bool finished() const { + const Snap s = active_->snapshot(); + return s.halted || s.fault; + } + + val toState(const Snap& s) const { + val st = val::object(); + st.set("cycle", cycles_); + st.set("halted", s.halted); + st.set("fault", s.fault); + val stages = val::array(); + static const char* kNames[5] = {"IF", "ID", "EX", "MEM", "WB"}; + for (uint32_t i = 0; i < 5; ++i) { + val stg = val::object(); + stg.set("name", std::string(kNames[i])); + stg.set("valid", static_cast(s.stage_valid >> i & 1)); + stg.set("pc", s.stage_pc[i]); + stg.set("insn", s.stage_insn[i]); + stages.call("push", stg); + } + st.set("stages", stages); + val hz = val::object(); + hz.set("stall", s.stall); + hz.set("loadUse", s.load_use); + hz.set("flushIFID", s.flush_ifid); + hz.set("flushIDEX", s.flush_idex); + hz.set("fwdA", static_cast(s.fwd_a)); + hz.set("fwdB", static_cast(s.fwd_b)); + st.set("hazard", hz); + val regs = val::array(); + for (uint32_t i = 0; i < 32; ++i) regs.call("push", s.regs[i]); + st.set("regs", regs); + if (s.rvfi_valid) { + val r = val::object(); + r.set("order", s.order); + r.set("pc", s.pc); + r.set("insn", s.insn); + r.set("rdAddr", static_cast(s.rd_addr)); + r.set("rdWdata", s.rd_wdata); + r.set("pcWdata", s.pc_wdata); + r.set("memAddr", s.mem_addr); + r.set("memRmask", static_cast(s.mem_rmask)); + r.set("memWmask", static_cast(s.mem_wmask)); + r.set("memRdata", s.mem_rdata); + r.set("memWdata", s.mem_wdata); + r.set("misaligned", s.rvfi_misaligned); + r.set("halt", s.rvfi_halt); + r.set("cycleDep", s.rvfi_cycle_dep); + st.set("retired", r); + } else { + st.set("retired", val::null()); + } + st.set("console", s.console_valid ? val(static_cast(s.console_data)) : val::null()); + st.set("tohost", s.tohost_valid ? val(s.tohost_data) : val::null()); + val counters = val::object(); + counters.set("cycle", cycles_); + counters.set("instret", s.instret); + st.set("counters", counters); + return st; + } + + std::unique_ptr> single_; + std::unique_ptr> pipe_; + ModelIface* active_ = nullptr; + bool forward_ = true; + bool started_ = false; + uint32_t cycles_ = 0; +}; diff --git a/wasm/test.mjs b/wasm/test.mjs new file mode 100644 index 0000000..aaec93a --- /dev/null +++ b/wasm/test.mjs @@ -0,0 +1,132 @@ +// Headless check of the WASM build (Phase 5). Run after `bash wasm/build.sh`: +// +// node wasm/test.mjs +// +// For every fixture in wasm/test/fixtures.json (generated from the golden ISS) and every core - SINGLE, +// and PIPE with forwarding on and off - it checks: the console output, the final registers (except +// timing-dependent ones), data memory, the instruction count, the SHA-256 of the retire stream, the +// halt/tohost behaviour, and the cycle count from the pipeline model. Exit status 0 = all good. +import { createHash } from "node:crypto"; +import { readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; + +const here = dirname(fileURLToPath(import.meta.url)); +const root = join(here, ".."); +const fixtures = JSON.parse(readFileSync(join(here, "test", "fixtures.json"), "utf8")); +const { default: createTinyCpu } = await import(pathToFileURL(join(root, "web", "public", "tiny_cpu.mjs")).href); +const mod = await createTinyCpu(); + +const MAX_CYCLES = 200_000; +let failures = 0; +let checks = 0; + +function check(cond, msg) { + checks += 1; + if (!cond) { + failures += 1; + console.error(` FAIL: ${msg}`); + } +} + +function words(hex) { + const b = Buffer.from(hex, "hex"); + const out = []; + for (let i = 0; i + 3 < b.length; i += 4) out.push(b.readUInt32LE(i)); + return out; +} + +// Must match sim/golden/wasm_fixtures.py retire_digest(): 13 little-endian u32 per retire record. +function retireDigest(states) { + const h = createHash("sha256"); + const buf = Buffer.alloc(13 * 4); + for (const s of states) { + const r = s.retired; + if (!r) continue; + const fields = [ + r.order, r.pc, r.insn, r.rdAddr, r.cycleDep ? 0 : r.rdWdata, r.pcWdata, r.memAddr, + r.memRmask, r.memWmask, r.cycleDep ? 0 : r.memRdata, r.memWdata, r.misaligned ? 1 : 0, r.halt ? 1 : 0, + ]; + fields.forEach((v, i) => buf.writeUInt32LE(v >>> 0, 4 * i)); + h.update(buf); + } + return h.digest("hex"); +} + +function runOne(name, fx, core, forward) { + const tag = `${name} ${core}${core === "PIPE" ? (forward ? " fwd" : " nofwd") : ""}`; + const sim = new mod.TinyCpuSim(); + try { + sim.setForwarding(forward); + check(sim.reset(core) === true, `${tag}: reset accepts ${core}`); + check(sim.loadImem(words(fx.imem)) === true, `${tag}: loadImem`); + check(sim.loadDmem(Array.from(Buffer.from(fx.dmem, "hex"))) === true, `${tag}: loadDmem`); + sim.start(); + const states = sim.run(MAX_CYCLES); + const last = states[states.length - 1]; + + check(last && last.halted && !last.fault, `${tag}: halted without fault`); + check(last.tohost === 1, `${tag}: tohost == 1 (PASS), got ${last.tohost}`); + check(states.every((s, i) => s.cycle === i + 1), `${tag}: cycle counter counts edges from 1`); + + const retired = states.filter((s) => s.retired).map((s) => s.retired); + check(retired.length === fx.instret, `${tag}: instret ${retired.length} != ${fx.instret}`); + check(last.counters.instret === fx.instret, `${tag}: counters.instret`); + + const console_ = states.filter((s) => s.console !== null).map((s) => String.fromCharCode(s.console)).join(""); + check(console_ === fx.console, `${tag}: console ${JSON.stringify(console_)} != ${JSON.stringify(fx.console)}`); + check(sim.getConsole() === fx.console, `${tag}: getConsole()`); + + const regs = sim.getRegs(); + const timing = new Set(fx.timingRegs); + for (let r = 0; r < 32; r++) { + if (!timing.has(r)) check(regs[r] === fx.regs[r], `${tag}: x${r} = ${regs[r]} != ${fx.regs[r]}`); + } + + if (fx.expect.dmemAddr !== undefined) { + const got = Buffer.from(sim.readDmem(fx.expect.dmemAddr, fx.expect.dmemHex.length / 2)).toString("hex"); + check(got === fx.expect.dmemHex, `${tag}: data memory ${got} != ${fx.expect.dmemHex}`); + } + + check(retireDigest(states) === fx.digest, `${tag}: retire digest mismatch`); + + const want = core === "SINGLE" ? fx.cycles.single : forward ? fx.cycles.pipeFwd : fx.cycles.pipeNoFwd; + check(states.length === want, `${tag}: ${states.length} cycles, model says ${want}`); + return states.length; + } finally { + sim.delete(); + } +} + +for (const [name, fx] of Object.entries(fixtures)) { + console.log(name); + runOne(name, fx, "SINGLE", true); + runOne(name, fx, "PIPE", true); + runOne(name, fx, "PIPE", false); +} + +// API edges: unknown core, oversized images, step() before start(), step() after halt. +{ + const sim = new mod.TinyCpuSim(); + check(sim.reset("MULTI") === false, "unknown core is rejected"); + check(sim.loadImem(new Array(1025).fill(0)) === false, "oversized IMEM is rejected"); + check(sim.loadDmem(new Array(4097).fill(0)) === false, "oversized DMEM is rejected"); + check(sim.step() === null, "step() before start() returns null"); + const fx = fixtures["hazard_free/fib"]; + sim.reset("SINGLE"); + sim.loadImem(words(fx.imem)); + sim.loadDmem(Array.from(Buffer.from(fx.dmem, "hex"))); + sim.start(); + const first = sim.step(); + check(first.cycle === 1 && first.halted === false, "first step is cycle 1"); + check(first.hazard.stall === false && first.stages[0].name === "IF", "state shape"); + const summary = sim.runFast(MAX_CYCLES); + check(summary.halted && summary.cycles === fixtures["hazard_free/fib"].cycles.single, "runFast cycle count"); + check(summary.tohost === 1 && summary.console === "", "runFast summary fields"); + const halted = sim.step(); + check(halted.halted && halted.cycle === summary.cycles, "step() after halt does not advance"); + sim.delete(); +} + +console.log(`${checks} checks, ${failures} failed`); +process.exit(failures === 0 ? 0 : 1); diff --git a/wasm/test/fixtures.json b/wasm/test/fixtures.json new file mode 100644 index 0000000..5186297 --- /dev/null +++ b/wasm/test/fixtures.json @@ -0,0 +1,204 @@ +{ + "hazard_dense/3": { + "console": "", + "cycles": { + "pipeFwd": 120, + "pipeNoFwd": 137, + "single": 107 + }, + "digest": "1f805214ead1ff4f7706b6ef0cd97545ed56ac44623342c85337c980e7808846", + "dmem": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "expect": {}, + "imem": "37040100130000001300000013040400b740eb3c13000000130000009380d0ff3751b797130000001300000013012109b7a1528b13000000130000009381a1b437626321130000001300000013029236b762b55e13000000130000009382421a130000001300000013000000b302520013f2c1709392e200a3215406b3005100a31d4404131272018310b4070352940493915201a30c340483411405b3d2314063963000a302340093e272299382811e830094070341840593a061411352624093d2f2408321b402934032c493b0c10e3382500093e1d12313b131072304440413b131fa8301d400230c3402a3081406930252916318310063e611000311b40393512200337221003703000013000000130000001303c312670003000342e406b3a032006f00c000b330120003018407b3e23000138200bd6f00800033b2420003516400638411008300d4068340840283206402f32120c003525406b3d022408302f404031254031362420a93a2608ea3173400a3261400b3f152009340b296b3d2500063843000830044002303540483207401130000001300000013000000b70f02001300000013000000938f4f00370f00001300000013000000130f1f00130000001300000023a0ef01", + "instret": 107, + "regs": [ + 0, + 0, + 0, + 0, + 164, + 4294965711, + 300, + 0, + 65536, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 131076 + ], + "timingRegs": [] + }, + "hazard_free/bubble_sort": { + "console": "", + "cycles": { + "pipeFwd": 863, + "pipeNoFwd": 863, + "single": 749 + }, + "digest": "50337c05646566cdc7fd48bba7efd065d3b71707a1b28125407878ffbb4fe53f", + "dmem": "2a0000000700000063000000030000001200000040000000010000001b000000", + "expect": { + "dmemAddr": 65536, + "dmemHex": "010000000300000007000000120000001b0000002a0000004000000063000000" + }, + "imem": "370401001300000013000000130404001300000013000000370900001300000013000000130979001300000013000000635a200b1300000013000000b7020000130000001300000093820200130000001300000013030400130000001300000063d6220713000000130000008325030013000000130000000326430013000000130000006352b60213000000130000002320c30013000000130000002322b30013000000130000009382120013000000130000001303430013000000130000006ff01ffa13000000130000001309f9ff13000000130000006ff09ff51300000013000000b70f02001300000013000000938f4f001300000013000000370f00001300000013000000130f1f00130000001300000023a0ef011300000013000000", + "instret": 749, + "regs": [ + 0, + 0, + 0, + 0, + 0, + 1, + 65540, + 0, + 65536, + 0, + 0, + 1, + 3, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 131076 + ], + "timingRegs": [] + }, + "hazard_free/console_and_csr": { + "console": "Hi, pipe!\n!", + "cycles": { + "pipeFwd": 218, + "pipeNoFwd": 218, + "single": 214 + }, + "digest": "6da00379929588d3923278b1ffdc8760d84c644b61ff270d0e04209dfa639386", + "dmem": "00000000000000000000000000000000", + "expect": {}, + "imem": "370a01001300000013000000130a0a001300000013000000370e02001300000013000000130e0e001300000013000000b70e00001300000013000000938e8e0413000000130000002300de011300000013000000370e02001300000013000000130e0e001300000013000000b70e00001300000013000000938e9e0613000000130000002300de011300000013000000370e02001300000013000000130e0e001300000013000000b70e00001300000013000000938ece0213000000130000002300de011300000013000000370e02001300000013000000130e0e001300000013000000b70e00001300000013000000938e0e0213000000130000002300de011300000013000000370e02001300000013000000130e0e001300000013000000b70e00001300000013000000938e0e0713000000130000002300de011300000013000000370e02001300000013000000130e0e001300000013000000b70e00001300000013000000938e9e0613000000130000002300de011300000013000000370e02001300000013000000130e0e001300000013000000b70e00001300000013000000938e0e0713000000130000002300de011300000013000000370e02001300000013000000130e0e001300000013000000b70e00001300000013000000938e5e0613000000130000002300de011300000013000000370e02001300000013000000130e0e001300000013000000b70e00001300000013000000938e1e0213000000130000002300de011300000013000000370e02001300000013000000130e0e001300000013000000b70e00001300000013000000938eae0013000000130000002300de011300000013000000f32a20c0130000001300000023205a011300000013000000732b00c01300000013000000f32b10c01300000013000000732c20c8130000001300000023228a011300000013000000b70c02001300000013000000938c8c00130000001300000003ad0c0013000000130000000f0000001300000013000000370e02001300000013000000130e0e001300000013000000b70e00001300000013000000938e1e0213000000130000002300de011300000013000000b70f02001300000013000000938f4f001300000013000000370f00001300000013000000130f1f00130000001300000023a0ef011300000013000000", + "instret": 214, + "regs": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 65536, + 156, + 162, + 165, + 0, + 131080, + 180, + 0, + 131072, + 33, + 1, + 131076 + ], + "timingRegs": [ + 22, + 23, + 26 + ] + }, + "hazard_free/fib": { + "console": "", + "cycles": { + "pipeFwd": 407, + "pipeNoFwd": 407, + "single": 361 + }, + "digest": "fef0c4b1e25420c741e7596fdbbcc81b6b398fb817689aeec6195c200cb51f80", + "dmem": "00000000", + "expect": { + "dmemAddr": 65536, + "dmemHex": "6d1a0000" + }, + "imem": "b70200001300000013000000938242011300000013000000370300001300000013000000130303001300000013000000b70300001300000013000000938313001300000013000000638402041300000013000000330e7300130000001300000013830300130000001300000093030e0013000000130000009382f2ff13000000130000006ff05ffc1300000013000000370401001300000013000000130404001300000013000000232064001300000013000000b70f02001300000013000000938f4f001300000013000000370f00001300000013000000130f1f00130000001300000023a0ef011300000013000000", + "instret": 361, + "regs": [ + 0, + 0, + 0, + 0, + 0, + 0, + 6765, + 10946, + 65536, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 10946, + 0, + 1, + 131076 + ], + "timingRegs": [] + } +} diff --git a/sim/programs/.gitkeep b/web/.github-placeholder similarity index 100% rename from sim/programs/.gitkeep rename to web/.github-placeholder diff --git a/web/.gitignore b/web/.gitignore index ab3d06f..acf423e 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -1,3 +1,5 @@ # Keep .vercel/project.json committed — it pins the project link so # re-linking is never needed. Only ignore the generated README inside. .vercel/README.txt +test-results/ +playwright-report/ diff --git a/web/e2e/app.spec.ts b/web/e2e/app.spec.ts new file mode 100644 index 0000000..711c1dd --- /dev/null +++ b/web/e2e/app.spec.ts @@ -0,0 +1,91 @@ +// Smoke tests for the browser app, run against the production build. The first preset runs once the WASM +// core has loaded, so each test waits for "N cycles recorded" and then moves the cursor where it needs. +import { expect, type Page, test } from "@playwright/test"; + +const recorded = (page: Page) => page.getByText(/\d+ cycles recorded/); +const halted = (page: Page) => page.getByText(/Halted at cycle \d+ — PASS\./); + +test("the app loads the core and runs the default program to PASS", async ({ page }) => { + const errors: string[] = []; + page.on("pageerror", (e) => errors.push(e.message)); + await page.goto("/app/"); + await expect(recorded(page)).toBeVisible({ timeout: 60_000 }); + await page.getByRole("button", { name: "Go to end" }).click(); + await expect(halted(page)).toBeVisible(); + await expect(page.locator("pre").filter({ hasText: "Hello, TinyCPU!" })).toBeVisible(); + expect(errors).toEqual([]); +}); + +test("the cycle controls step backwards and forwards", async ({ page }) => { + await page.goto("/app/"); + await expect(recorded(page)).toBeVisible({ timeout: 60_000 }); + await page.getByRole("button", { name: "Step forward" }).click(); + await expect(page.getByText(/^Cycle 2 of \d+\.$/)).toBeVisible(); + await page.getByRole("button", { name: "Step back" }).click(); + await expect(page.getByText(/^Cycle 1 of \d+\.$/)).toBeVisible(); + await page.getByRole("button", { name: "Go to end" }).click(); + await expect(halted(page)).toBeVisible(); + await page.getByRole("button", { name: "Go to start" }).click(); + await expect(page.getByText(/^Cycle 1 of \d+\.$/)).toBeVisible(); +}); + +test("an assembly error is reported with its line number", async ({ page }) => { + await page.goto("/app/"); + await expect(recorded(page)).toBeVisible({ timeout: 60_000 }); + await page.getByLabel("Assembly source").fill("_start:\n frobnicate a0\n"); + await page.getByRole("button", { name: "Assemble & run" }).click(); + await expect(page.getByRole("alert")).toContainText("line 2: unknown instruction 'frobnicate'"); +}); + +test("another preset prints its answer", async ({ page }) => { + await page.goto("/app/"); + await expect(recorded(page)).toBeVisible({ timeout: 60_000 }); + await page.getByLabel("Program").selectOption("fib"); + await page.getByRole("button", { name: "Assemble & run" }).click(); + await page.getByRole("button", { name: "Go to end" }).click(); + await expect(page.locator("pre").filter({ hasText: "6765" })).toBeVisible(); + await expect(halted(page)).toBeVisible(); +}); + +test("the hazard lab shows the measured forwarding difference", async ({ page }) => { + await page.goto("/app/"); + await expect(recorded(page)).toBeVisible({ timeout: 60_000 }); + await page.getByRole("tab", { name: "L3 Hazard lab" }).click(); + await page.getByRole("button", { name: "ALU to ALU, distance 1" }).click(); + await expect(page.getByRole("cell", { name: "5-stage, forwarding on" })).toBeVisible(); + await expect(page.getByRole("cell", { name: "single-cycle" })).toBeVisible(); +}); + +test("the datapath view shows the instruction that retired", async ({ page }) => { + await page.goto("/app/"); + await expect(recorded(page)).toBeVisible({ timeout: 60_000 }); + await page.getByRole("tab", { name: "L1 Datapath" }).click(); + await page.getByRole("button", { name: "Go to end" }).click(); + await expect(page.getByRole("img", { name: /Single-cycle datapath for/ })).toBeVisible(); + await expect(page.getByText(/retire #/)).toBeVisible(); +}); + +test("the programs view lists every preset with PASS", async ({ page }) => { + await page.goto("/app/"); + await expect(recorded(page)).toBeVisible({ timeout: 60_000 }); + await page.getByRole("tab", { name: "L4 Programs" }).click(); + await expect(page.getByRole("cell", { name: "PASS" })).toHaveCount(5); + await expect(page.getByRole("img", { name: "Data memory access heatmap" })).toBeVisible(); +}); + +test("the layout has no horizontal page scroll at phone width", async ({ page }) => { + await page.setViewportSize({ width: 390, height: 844 }); + await page.goto("/app/"); + await expect(recorded(page)).toBeVisible({ timeout: 60_000 }); + const overflow = await page.evaluate(() => document.documentElement.scrollWidth - document.documentElement.clientWidth); + expect(overflow).toBeLessThanOrEqual(1); +}); + +test("the theme toggle switches the colour scheme", async ({ page }) => { + await page.goto("/app/"); + await expect(recorded(page)).toBeVisible({ timeout: 60_000 }); + const before = await page.evaluate(() => document.documentElement.getAttribute("data-theme")); + await page.getByRole("button", { name: /Switch to (light|dark) theme/ }).click(); + const after = await page.evaluate(() => document.documentElement.getAttribute("data-theme")); + expect(after).not.toBe(before); +}); diff --git a/web/package.json b/web/package.json index 6214973..8e0e7c4 100644 --- a/web/package.json +++ b/web/package.json @@ -8,7 +8,9 @@ "preview": "astro preview", "lint": "eslint .", "typecheck": "astro check && tsc --noEmit", - "format": "prettier --write ." + "format": "prettier --write .", + "test": "vitest run", + "test:e2e": "playwright test" }, "dependencies": { "@astrojs/react": "^4.4.2", @@ -24,6 +26,7 @@ "devDependencies": { "@astrojs/check": "^0.9.4", "@eslint/js": "^9.13.0", + "@playwright/test": "1.56.1", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@typescript-eslint/eslint-plugin": "^8.12.2", @@ -34,6 +37,7 @@ "globals": "^17.6.0", "prettier": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.8", - "typescript": "^5.6.3" + "typescript": "^5.6.3", + "vitest": "3.2.4" } } diff --git a/web/playwright.config.ts b/web/playwright.config.ts new file mode 100644 index 0000000..d2fc3a1 --- /dev/null +++ b/web/playwright.config.ts @@ -0,0 +1,18 @@ +import { defineConfig, devices } from "@playwright/test"; + +// The smoke test serves the production build (`pnpm build`) with `astro preview`. +// Browsers: `pnpm exec playwright install chromium` once (CI does this). +export default defineConfig({ + testDir: "e2e", + timeout: 60_000, + fullyParallel: true, + reporter: [["list"]], + use: { baseURL: "http://localhost:4321", trace: "retain-on-failure" }, + projects: [{ name: "chromium", use: { ...devices["Desktop Chrome"] } }], + webServer: { + command: "pnpm preview --port 4321 --host localhost", + url: "http://localhost:4321/app/", + reuseExistingServer: true, + timeout: 120_000, + }, +}); diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 8128631..5060e3b 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -1,19 +1,20 @@ -lockfileVersion: "9.0" +lockfileVersion: '9.0' settings: autoInstallPeers: true excludeLinksFromLockfile: false importers: + .: dependencies: - "@astrojs/react": + '@astrojs/react': specifier: ^4.4.2 version: 4.4.2(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(jiti@2.7.0)(lightningcss@1.32.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@7.2.0)(yaml@2.9.1) - "@fontsource-variable/inter": + '@fontsource-variable/inter': specifier: ^5.0.0 version: 5.3.0 - "@tailwindcss/vite": + '@tailwindcss/vite': specifier: ^4.0.0 version: 4.3.3(vite@6.4.5(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.1)) astro: @@ -35,22 +36,25 @@ importers: specifier: ^4.0.0 version: 4.3.3 devDependencies: - "@astrojs/check": + '@astrojs/check': specifier: ^0.9.4 version: 0.9.12(@emnapi/runtime@1.11.3)(prettier@3.9.10)(typescript@5.9.3) - "@eslint/js": + '@eslint/js': specifier: ^9.13.0 version: 9.39.5 - "@types/react": + '@playwright/test': + specifier: 1.56.1 + version: 1.56.1 + '@types/react': specifier: ^18.3.12 version: 18.3.31 - "@types/react-dom": + '@types/react-dom': specifier: ^18.3.1 version: 18.3.7(@types/react@18.3.31) - "@typescript-eslint/eslint-plugin": + '@typescript-eslint/eslint-plugin': specifier: ^8.12.2 version: 8.72.0(@typescript-eslint/parser@8.72.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) - "@typescript-eslint/parser": + '@typescript-eslint/parser': specifier: ^8.12.2 version: 8.72.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) eslint: @@ -74,1855 +78,1218 @@ importers: typescript: specifier: ^5.6.3 version: 5.9.3 + vitest: + specifier: 3.2.4 + version: 3.2.4(@types/debug@4.1.13)(jiti@2.7.0)(lightningcss@1.32.0)(supports-color@7.2.0)(yaml@2.9.1) packages: - "@astrojs/astro2tsx@0.1.3": - resolution: - { - integrity: sha512-GMDC7Eo0myBUA/QLBl0u+OcegJ8LiBhccmNZR34KmprQNP8gQUJlWlOOZWUsxNA2rNS6GuPYcGDyLoPnjmn4/Q==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - - "@astrojs/check@0.9.12": - resolution: - { - integrity: sha512-hazpqxIfZWj2VhyPyR0pOEQ/IhFDM/2nIApL96zMXABdD8NrUKYDmhxTy7h+drTDl9MQG/Sd1ET/4amadVJUUA==, - } + + '@astrojs/astro2tsx@0.1.3': + resolution: {integrity: sha512-GMDC7Eo0myBUA/QLBl0u+OcegJ8LiBhccmNZR34KmprQNP8gQUJlWlOOZWUsxNA2rNS6GuPYcGDyLoPnjmn4/Q==} + engines: {node: ^20.19.0 || >=22.12.0} + + '@astrojs/check@0.9.12': + resolution: {integrity: sha512-hazpqxIfZWj2VhyPyR0pOEQ/IhFDM/2nIApL96zMXABdD8NrUKYDmhxTy7h+drTDl9MQG/Sd1ET/4amadVJUUA==} hasBin: true peerDependencies: typescript: ^5.0.0 || ^6.0.0 - "@astrojs/compiler@2.13.1": - resolution: - { - integrity: sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==, - } - - "@astrojs/internal-helpers@0.7.6": - resolution: - { - integrity: sha512-GOle7smBWKfMSP8osUIGOlB5kaHdQLV3foCsf+5Q9Wsuu+C6Fs3Ez/ttXmhjZ1HkSgsogcM1RXSjjOVieHq16Q==, - } - - "@astrojs/language-server@2.17.3": - resolution: - { - integrity: sha512-QIAFu1e/4quJMTPdmBOm+ZPXc5NDGZE4/YQjgR3EgjhGLl/tdidaWJDr5WmO2lesVc+BW1qN/xNBURmuePDG2A==, - } + '@astrojs/compiler@2.13.1': + resolution: {integrity: sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==} + + '@astrojs/internal-helpers@0.7.6': + resolution: {integrity: sha512-GOle7smBWKfMSP8osUIGOlB5kaHdQLV3foCsf+5Q9Wsuu+C6Fs3Ez/ttXmhjZ1HkSgsogcM1RXSjjOVieHq16Q==} + + '@astrojs/language-server@2.17.3': + resolution: {integrity: sha512-QIAFu1e/4quJMTPdmBOm+ZPXc5NDGZE4/YQjgR3EgjhGLl/tdidaWJDr5WmO2lesVc+BW1qN/xNBURmuePDG2A==} hasBin: true peerDependencies: prettier: ^3.0.0 - prettier-plugin-astro: ">=0.11.0" + prettier-plugin-astro: '>=0.11.0' peerDependenciesMeta: prettier: optional: true prettier-plugin-astro: optional: true - "@astrojs/markdown-remark@6.3.11": - resolution: - { - integrity: sha512-hcaxX/5aC6lQgHeGh1i+aauvSwIT6cfyFjKWvExYSxUhZZBBdvCliOtu06gbQyhbe0pGJNoNmqNlQZ5zYUuIyQ==, - } - - "@astrojs/prism@3.3.0": - resolution: - { - integrity: sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==, - } - engines: { node: 18.20.8 || ^20.3.0 || >=22.0.0 } - - "@astrojs/react@4.4.2": - resolution: - { - integrity: sha512-1tl95bpGfuaDMDn8O3x/5Dxii1HPvzjvpL2YTuqOOrQehs60I2DKiDgh1jrKc7G8lv+LQT5H15V6QONQ+9waeQ==, - } - engines: { node: 18.20.8 || ^20.3.0 || >=22.0.0 } + '@astrojs/markdown-remark@6.3.11': + resolution: {integrity: sha512-hcaxX/5aC6lQgHeGh1i+aauvSwIT6cfyFjKWvExYSxUhZZBBdvCliOtu06gbQyhbe0pGJNoNmqNlQZ5zYUuIyQ==} + + '@astrojs/prism@3.3.0': + resolution: {integrity: sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + + '@astrojs/react@4.4.2': + resolution: {integrity: sha512-1tl95bpGfuaDMDn8O3x/5Dxii1HPvzjvpL2YTuqOOrQehs60I2DKiDgh1jrKc7G8lv+LQT5H15V6QONQ+9waeQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} peerDependencies: - "@types/react": ^17.0.50 || ^18.0.21 || ^19.0.0 - "@types/react-dom": ^17.0.17 || ^18.0.6 || ^19.0.0 + '@types/react': ^17.0.50 || ^18.0.21 || ^19.0.0 + '@types/react-dom': ^17.0.17 || ^18.0.6 || ^19.0.0 react: ^17.0.2 || ^18.0.0 || ^19.0.0 react-dom: ^17.0.2 || ^18.0.0 || ^19.0.0 - "@astrojs/telemetry@3.3.0": - resolution: - { - integrity: sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==, - } - engines: { node: 18.20.8 || ^20.3.0 || >=22.0.0 } - - "@astrojs/yaml2ts@0.2.4": - resolution: - { - integrity: sha512-8oddpOae35pJsXPQXhTkM0ypfKPskVsh2bCxRtbf7e+/Epw2nReakFYpLKjZMEr75CsoF203PMnCocpfz0s69A==, - } - - "@babel/code-frame@7.29.7": - resolution: - { - integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==, - } - engines: { node: ">=6.9.0" } - - "@babel/compat-data@7.29.7": - resolution: - { - integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==, - } - engines: { node: ">=6.9.0" } - - "@babel/core@7.29.7": - resolution: - { - integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==, - } - engines: { node: ">=6.9.0" } - - "@babel/generator@7.29.8": - resolution: - { - integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-compilation-targets@7.29.7": - resolution: - { - integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-globals@7.29.7": - resolution: - { - integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-module-imports@7.29.7": - resolution: - { - integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-module-transforms@7.29.7": - resolution: - { - integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==, - } - engines: { node: ">=6.9.0" } + '@astrojs/telemetry@3.3.0': + resolution: {integrity: sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + + '@astrojs/yaml2ts@0.2.4': + resolution: {integrity: sha512-8oddpOae35pJsXPQXhTkM0ypfKPskVsh2bCxRtbf7e+/Epw2nReakFYpLKjZMEr75CsoF203PMnCocpfz0s69A==} + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} peerDependencies: - "@babel/core": ^7.0.0 - - "@babel/helper-plugin-utils@7.29.7": - resolution: - { - integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-string-parser@7.29.7": - resolution: - { - integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-validator-identifier@7.29.7": - resolution: - { - integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-validator-option@7.29.7": - resolution: - { - integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==, - } - engines: { node: ">=6.9.0" } - - "@babel/helpers@7.29.10": - resolution: - { - integrity: sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==, - } - engines: { node: ">=6.9.0" } - - "@babel/parser@7.29.9": - resolution: - { - integrity: sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==, - } - engines: { node: ">=6.0.0" } + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.10': + resolution: {integrity: sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.9': + resolution: {integrity: sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==} + engines: {node: '>=6.0.0'} hasBin: true - "@babel/plugin-transform-react-jsx-self@7.29.7": - resolution: - { - integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==, - } - engines: { node: ">=6.9.0" } + '@babel/plugin-transform-react-jsx-self@7.29.7': + resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==} + engines: {node: '>=6.9.0'} peerDependencies: - "@babel/core": ^7.0.0-0 - - "@babel/plugin-transform-react-jsx-source@7.29.7": - resolution: - { - integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==, - } - engines: { node: ">=6.9.0" } + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.29.7': + resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==} + engines: {node: '>=6.9.0'} peerDependencies: - "@babel/core": ^7.0.0-0 - - "@babel/template@7.29.7": - resolution: - { - integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==, - } - engines: { node: ">=6.9.0" } - - "@babel/traverse@7.29.10": - resolution: - { - integrity: sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==, - } - engines: { node: ">=6.9.0" } - - "@babel/types@7.29.8": - resolution: - { - integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==, - } - engines: { node: ">=6.9.0" } - - "@capsizecss/unpack@4.0.1": - resolution: - { - integrity: sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==, - } - engines: { node: ">=18" } - - "@emmetio/abbreviation@2.3.3": - resolution: - { - integrity: sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==, - } - - "@emmetio/css-abbreviation@2.1.8": - resolution: - { - integrity: sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==, - } - - "@emmetio/css-parser@0.4.1": - resolution: - { - integrity: sha512-2bC6m0MV/voF4CTZiAbG5MWKbq5EBmDPKu9Sb7s7nVcEzNQlrZP6mFFFlIaISM8X6514H9shWMme1fCm8cWAfQ==, - } - - "@emmetio/html-matcher@1.3.0": - resolution: - { - integrity: sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ==, - } - - "@emmetio/scanner@1.0.4": - resolution: - { - integrity: sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==, - } - - "@emmetio/stream-reader-utils@0.1.0": - resolution: - { - integrity: sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A==, - } - - "@emmetio/stream-reader@2.2.0": - resolution: - { - integrity: sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw==, - } - - "@emnapi/core@1.11.3": - resolution: - { - integrity: sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==, - } - - "@emnapi/runtime@1.11.3": - resolution: - { - integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==, - } - - "@emnapi/wasi-threads@1.2.3": - resolution: - { - integrity: sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==, - } - - "@esbuild/aix-ppc64@0.25.12": - resolution: - { - integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==, - } - engines: { node: ">=18" } + '@babel/core': ^7.0.0-0 + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.10': + resolution: {integrity: sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@capsizecss/unpack@4.0.1': + resolution: {integrity: sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==} + engines: {node: '>=18'} + + '@emmetio/abbreviation@2.3.3': + resolution: {integrity: sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==} + + '@emmetio/css-abbreviation@2.1.8': + resolution: {integrity: sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==} + + '@emmetio/css-parser@0.4.1': + resolution: {integrity: sha512-2bC6m0MV/voF4CTZiAbG5MWKbq5EBmDPKu9Sb7s7nVcEzNQlrZP6mFFFlIaISM8X6514H9shWMme1fCm8cWAfQ==} + + '@emmetio/html-matcher@1.3.0': + resolution: {integrity: sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ==} + + '@emmetio/scanner@1.0.4': + resolution: {integrity: sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==} + + '@emmetio/stream-reader-utils@0.1.0': + resolution: {integrity: sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A==} + + '@emmetio/stream-reader@2.2.0': + resolution: {integrity: sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw==} + + '@emnapi/core@1.11.3': + resolution: {integrity: sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==} + + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + + '@emnapi/wasi-threads@1.2.3': + resolution: {integrity: sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==} + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] - "@esbuild/aix-ppc64@0.27.7": - resolution: - { - integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==, - } - engines: { node: ">=18" } + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] - "@esbuild/android-arm64@0.25.12": - resolution: - { - integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==, - } - engines: { node: ">=18" } + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} cpu: [arm64] os: [android] - "@esbuild/android-arm64@0.27.7": - resolution: - { - integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==, - } - engines: { node: ">=18" } + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + engines: {node: '>=18'} cpu: [arm64] os: [android] - "@esbuild/android-arm@0.25.12": - resolution: - { - integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==, - } - engines: { node: ">=18" } + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} cpu: [arm] os: [android] - "@esbuild/android-arm@0.27.7": - resolution: - { - integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==, - } - engines: { node: ">=18" } + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} + engines: {node: '>=18'} cpu: [arm] os: [android] - "@esbuild/android-x64@0.25.12": - resolution: - { - integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==, - } - engines: { node: ">=18" } + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} cpu: [x64] os: [android] - "@esbuild/android-x64@0.27.7": - resolution: - { - integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==, - } - engines: { node: ">=18" } + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} + engines: {node: '>=18'} cpu: [x64] os: [android] - "@esbuild/darwin-arm64@0.25.12": - resolution: - { - integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==, - } - engines: { node: ">=18" } + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - "@esbuild/darwin-arm64@0.27.7": - resolution: - { - integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==, - } - engines: { node: ">=18" } + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - "@esbuild/darwin-x64@0.25.12": - resolution: - { - integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==, - } - engines: { node: ">=18" } + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - "@esbuild/darwin-x64@0.27.7": - resolution: - { - integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==, - } - engines: { node: ">=18" } + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - "@esbuild/freebsd-arm64@0.25.12": - resolution: - { - integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==, - } - engines: { node: ">=18" } + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - "@esbuild/freebsd-arm64@0.27.7": - resolution: - { - integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==, - } - engines: { node: ">=18" } + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - "@esbuild/freebsd-x64@0.25.12": - resolution: - { - integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==, - } - engines: { node: ">=18" } + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] - "@esbuild/freebsd-x64@0.27.7": - resolution: - { - integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==, - } - engines: { node: ">=18" } + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] - "@esbuild/linux-arm64@0.25.12": - resolution: - { - integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==, - } - engines: { node: ">=18" } + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] - "@esbuild/linux-arm64@0.27.7": - resolution: - { - integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==, - } - engines: { node: ">=18" } + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] - "@esbuild/linux-arm@0.25.12": - resolution: - { - integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==, - } - engines: { node: ">=18" } + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} cpu: [arm] os: [linux] - "@esbuild/linux-arm@0.27.7": - resolution: - { - integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==, - } - engines: { node: ">=18" } + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + engines: {node: '>=18'} cpu: [arm] os: [linux] - "@esbuild/linux-ia32@0.25.12": - resolution: - { - integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==, - } - engines: { node: ">=18" } + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] - "@esbuild/linux-ia32@0.27.7": - resolution: - { - integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==, - } - engines: { node: ">=18" } + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] - "@esbuild/linux-loong64@0.25.12": - resolution: - { - integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==, - } - engines: { node: ">=18" } + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] - "@esbuild/linux-loong64@0.27.7": - resolution: - { - integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==, - } - engines: { node: ">=18" } + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] - "@esbuild/linux-mips64el@0.25.12": - resolution: - { - integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==, - } - engines: { node: ">=18" } + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] - "@esbuild/linux-mips64el@0.27.7": - resolution: - { - integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==, - } - engines: { node: ">=18" } + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] - "@esbuild/linux-ppc64@0.25.12": - resolution: - { - integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==, - } - engines: { node: ">=18" } + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] - "@esbuild/linux-ppc64@0.27.7": - resolution: - { - integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==, - } - engines: { node: ">=18" } + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] - "@esbuild/linux-riscv64@0.25.12": - resolution: - { - integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==, - } - engines: { node: ">=18" } + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] - "@esbuild/linux-riscv64@0.27.7": - resolution: - { - integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==, - } - engines: { node: ">=18" } + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] - "@esbuild/linux-s390x@0.25.12": - resolution: - { - integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==, - } - engines: { node: ">=18" } + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] - "@esbuild/linux-s390x@0.27.7": - resolution: - { - integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==, - } - engines: { node: ">=18" } + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] - "@esbuild/linux-x64@0.25.12": - resolution: - { - integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==, - } - engines: { node: ">=18" } + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - "@esbuild/linux-x64@0.27.7": - resolution: - { - integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==, - } - engines: { node: ">=18" } + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - "@esbuild/netbsd-arm64@0.25.12": - resolution: - { - integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==, - } - engines: { node: ">=18" } + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - "@esbuild/netbsd-arm64@0.27.7": - resolution: - { - integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==, - } - engines: { node: ">=18" } + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - "@esbuild/netbsd-x64@0.25.12": - resolution: - { - integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==, - } - engines: { node: ">=18" } + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] - "@esbuild/netbsd-x64@0.27.7": - resolution: - { - integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==, - } - engines: { node: ">=18" } + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] - "@esbuild/openbsd-arm64@0.25.12": - resolution: - { - integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==, - } - engines: { node: ">=18" } + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - "@esbuild/openbsd-arm64@0.27.7": - resolution: - { - integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==, - } - engines: { node: ">=18" } + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} + engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - "@esbuild/openbsd-x64@0.25.12": - resolution: - { - integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==, - } - engines: { node: ">=18" } + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] - "@esbuild/openbsd-x64@0.27.7": - resolution: - { - integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==, - } - engines: { node: ">=18" } + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] - "@esbuild/openharmony-arm64@0.25.12": - resolution: - { - integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==, - } - engines: { node: ">=18" } + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - "@esbuild/openharmony-arm64@0.27.7": - resolution: - { - integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==, - } - engines: { node: ">=18" } + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - "@esbuild/sunos-x64@0.25.12": - resolution: - { - integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==, - } - engines: { node: ">=18" } + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] - "@esbuild/sunos-x64@0.27.7": - resolution: - { - integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==, - } - engines: { node: ">=18" } + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] - "@esbuild/win32-arm64@0.25.12": - resolution: - { - integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==, - } - engines: { node: ">=18" } + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - "@esbuild/win32-arm64@0.27.7": - resolution: - { - integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==, - } - engines: { node: ">=18" } + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - "@esbuild/win32-ia32@0.25.12": - resolution: - { - integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==, - } - engines: { node: ">=18" } + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] - "@esbuild/win32-ia32@0.27.7": - resolution: - { - integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==, - } - engines: { node: ">=18" } + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] - "@esbuild/win32-x64@0.25.12": - resolution: - { - integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==, - } - engines: { node: ">=18" } + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} cpu: [x64] os: [win32] - "@esbuild/win32-x64@0.27.7": - resolution: - { - integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==, - } - engines: { node: ">=18" } + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} cpu: [x64] os: [win32] - "@eslint-community/eslint-utils@4.10.1": - resolution: - { - integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + '@eslint-community/eslint-utils@4.10.1': + resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - "@eslint-community/regexpp@4.12.2": - resolution: - { - integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==, - } - engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } - - "@eslint/config-array@0.21.2": - resolution: - { - integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@eslint/config-helpers@0.4.2": - resolution: - { - integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@eslint/core@0.17.0": - resolution: - { - integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@eslint/eslintrc@3.3.7": - resolution: - { - integrity: sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@eslint/js@9.39.5": - resolution: - { - integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@eslint/object-schema@2.1.7": - resolution: - { - integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@eslint/plugin-kit@0.4.1": - resolution: - { - integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@fontsource-variable/inter@5.3.0": - resolution: - { - integrity: sha512-OupL48va4JNofb97w6NYeF9S7W/kHNKM0Er8Dem5nqi4jeOLrVJDoE8tZEpnMJmtkvNbB1EIPPwHcdkF6b1oUA==, - } - - "@humanfs/core@0.19.2": - resolution: - { - integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==, - } - engines: { node: ">=18.18.0" } - - "@humanfs/node@0.16.8": - resolution: - { - integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==, - } - engines: { node: ">=18.18.0" } - - "@humanfs/types@0.15.0": - resolution: - { - integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==, - } - engines: { node: ">=18.18.0" } - - "@humanwhocodes/module-importer@1.0.1": - resolution: - { - integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==, - } - engines: { node: ">=12.22" } - - "@humanwhocodes/retry@0.4.3": - resolution: - { - integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==, - } - engines: { node: ">=18.18" } - - "@img/colour@1.1.0": - resolution: - { - integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==, - } - engines: { node: ">=18" } - - "@img/sharp-darwin-arm64@0.34.5": - resolution: - { - integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.7': + resolution: {integrity: sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.5': + resolution: {integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@fontsource-variable/inter@5.3.0': + resolution: {integrity: sha512-OupL48va4JNofb97w6NYeF9S7W/kHNKM0Er8Dem5nqi4jeOLrVJDoE8tZEpnMJmtkvNbB1EIPPwHcdkF6b1oUA==} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] - "@img/sharp-darwin-x64@0.34.5": - resolution: - { - integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] - "@img/sharp-libvips-darwin-arm64@1.2.4": - resolution: - { - integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==, - } + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} cpu: [arm64] os: [darwin] - "@img/sharp-libvips-darwin-x64@1.2.4": - resolution: - { - integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==, - } + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} cpu: [x64] os: [darwin] - "@img/sharp-libvips-linux-arm64@1.2.4": - resolution: - { - integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==, - } + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] libc: [glibc] - "@img/sharp-libvips-linux-arm@1.2.4": - resolution: - { - integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==, - } + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] libc: [glibc] - "@img/sharp-libvips-linux-ppc64@1.2.4": - resolution: - { - integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==, - } + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] libc: [glibc] - "@img/sharp-libvips-linux-riscv64@1.2.4": - resolution: - { - integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==, - } + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] libc: [glibc] - "@img/sharp-libvips-linux-s390x@1.2.4": - resolution: - { - integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==, - } + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] libc: [glibc] - "@img/sharp-libvips-linux-x64@1.2.4": - resolution: - { - integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==, - } + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] libc: [glibc] - "@img/sharp-libvips-linuxmusl-arm64@1.2.4": - resolution: - { - integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==, - } + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] libc: [musl] - "@img/sharp-libvips-linuxmusl-x64@1.2.4": - resolution: - { - integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==, - } + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] libc: [musl] - "@img/sharp-linux-arm64@0.34.5": - resolution: - { - integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] libc: [glibc] - "@img/sharp-linux-arm@0.34.5": - resolution: - { - integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] libc: [glibc] - "@img/sharp-linux-ppc64@0.34.5": - resolution: - { - integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] libc: [glibc] - "@img/sharp-linux-riscv64@0.34.5": - resolution: - { - integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] libc: [glibc] - "@img/sharp-linux-s390x@0.34.5": - resolution: - { - integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] libc: [glibc] - "@img/sharp-linux-x64@0.34.5": - resolution: - { - integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] libc: [glibc] - "@img/sharp-linuxmusl-arm64@0.34.5": - resolution: - { - integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] libc: [musl] - "@img/sharp-linuxmusl-x64@0.34.5": - resolution: - { - integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] libc: [musl] - "@img/sharp-wasm32@0.34.5": - resolution: - { - integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - "@img/sharp-win32-arm64@0.34.5": - resolution: - { - integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [win32] - "@img/sharp-win32-ia32@0.34.5": - resolution: - { - integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] - "@img/sharp-win32-x64@0.34.5": - resolution: - { - integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] - "@jridgewell/gen-mapping@0.3.13": - resolution: - { - integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==, - } - - "@jridgewell/remapping@2.3.5": - resolution: - { - integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==, - } - - "@jridgewell/resolve-uri@3.1.2": - resolution: - { - integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, - } - engines: { node: ">=6.0.0" } - - "@jridgewell/sourcemap-codec@1.6.0": - resolution: - { - integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==, - } - - "@jridgewell/trace-mapping@0.3.31": - resolution: - { - integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==, - } - - "@napi-rs/lzma-linux-x64-gnu@1.5.1": - resolution: - { - integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==, - } - engines: { node: ^22.20 || ^24.12 || >=25 } + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==} + engines: {node: ^22.20 || ^24.12 || >=25} cpu: [x64] os: [linux] libc: [glibc] - "@napi-rs/wasm-runtime@1.2.4": - resolution: - { - integrity: sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g==, - } - engines: { node: ^20.19.0 || ^22.13.0 || >=23.5.0 } + '@napi-rs/wasm-runtime@1.2.4': + resolution: {integrity: sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} peerDependencies: - "@emnapi/core": ^1.7.1 || ^2.0.0-alpha.4 - "@emnapi/runtime": ^1.7.1 || ^2.0.0-alpha.4 - - "@oslojs/encoding@1.1.0": - resolution: - { - integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==, - } - - "@rolldown/pluginutils@1.0.0-beta.27": - resolution: - { - integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==, - } - - "@rollup/pluginutils@5.5.1": - resolution: - { - integrity: sha512-P9ZvnfouRu4MSC+ilERaw6FmZs8zqKhpHxEs4aGDGWQAMzQGXnhvCSvoNwokfNvdt7t3Y5Y4p6gStQgezeIqug==, - } - engines: { node: ">=14.0.0" } + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.4 + + '@oslojs/encoding@1.1.0': + resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} + + '@playwright/test@1.56.1': + resolution: {integrity: sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==} + engines: {node: '>=18'} + hasBin: true + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/pluginutils@5.5.1': + resolution: {integrity: sha512-P9ZvnfouRu4MSC+ilERaw6FmZs8zqKhpHxEs4aGDGWQAMzQGXnhvCSvoNwokfNvdt7t3Y5Y4p6gStQgezeIqug==} + engines: {node: '>=14.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true - "@rollup/rollup-android-arm-eabi@4.65.0": - resolution: - { - integrity: sha512-t2AnVba25baFdq7gczzS13JyVcdubLVbFX1X04mxrggDrTQPUXNv3XM0k9H1hr5tqvg7AVvtQ720QHAj0iYqhg==, - } + '@rollup/rollup-android-arm-eabi@4.65.0': + resolution: {integrity: sha512-t2AnVba25baFdq7gczzS13JyVcdubLVbFX1X04mxrggDrTQPUXNv3XM0k9H1hr5tqvg7AVvtQ720QHAj0iYqhg==} cpu: [arm] os: [android] - "@rollup/rollup-android-arm64@4.65.0": - resolution: - { - integrity: sha512-dNONUaEwPGhl/nuO2KV8VABWlcjYyU6KtZGN8dQv5OS5JU7JSCxPijVNKXbRJd0iuTzTiJ4TjPt/4bbvDRNiMQ==, - } + '@rollup/rollup-android-arm64@4.65.0': + resolution: {integrity: sha512-dNONUaEwPGhl/nuO2KV8VABWlcjYyU6KtZGN8dQv5OS5JU7JSCxPijVNKXbRJd0iuTzTiJ4TjPt/4bbvDRNiMQ==} cpu: [arm64] os: [android] - "@rollup/rollup-darwin-arm64@4.65.0": - resolution: - { - integrity: sha512-zWQqUH+9zavr2Bxuzfpuv2SIF3X31cr58TvSQC9o1GhpOvkqRwr60GkZdQcudyT/BEgdpdSafjgd2I13MbTL7g==, - } + '@rollup/rollup-darwin-arm64@4.65.0': + resolution: {integrity: sha512-zWQqUH+9zavr2Bxuzfpuv2SIF3X31cr58TvSQC9o1GhpOvkqRwr60GkZdQcudyT/BEgdpdSafjgd2I13MbTL7g==} cpu: [arm64] os: [darwin] - "@rollup/rollup-darwin-x64@4.65.0": - resolution: - { - integrity: sha512-RQYreBFWtO4//hIuOaZkYUnF/lPU4lz35dveDimDshWCJvVisVtJ/shmT9bkXK8wFJY+FJF89X6F3Cq1qI3LCw==, - } + '@rollup/rollup-darwin-x64@4.65.0': + resolution: {integrity: sha512-RQYreBFWtO4//hIuOaZkYUnF/lPU4lz35dveDimDshWCJvVisVtJ/shmT9bkXK8wFJY+FJF89X6F3Cq1qI3LCw==} cpu: [x64] os: [darwin] - "@rollup/rollup-freebsd-arm64@4.65.0": - resolution: - { - integrity: sha512-A0NfMj6Cuh/9BE5k3awJKv6HoxFVXIcLAb6SZqAMoxUHPA1ZNNK4RsGuUPD/vL7nEAm9LG+ZzXWWTrnC0XtkpA==, - } + '@rollup/rollup-freebsd-arm64@4.65.0': + resolution: {integrity: sha512-A0NfMj6Cuh/9BE5k3awJKv6HoxFVXIcLAb6SZqAMoxUHPA1ZNNK4RsGuUPD/vL7nEAm9LG+ZzXWWTrnC0XtkpA==} cpu: [arm64] os: [freebsd] - "@rollup/rollup-freebsd-x64@4.65.0": - resolution: - { - integrity: sha512-BI6/H7q3aP0I7S7GOrjyxTjgEJlSnovXE38+bdxBpuKN6z+d//uF+T+VQ3Q7ltJL/71p17ufartARX9Ja5gOGQ==, - } + '@rollup/rollup-freebsd-x64@4.65.0': + resolution: {integrity: sha512-BI6/H7q3aP0I7S7GOrjyxTjgEJlSnovXE38+bdxBpuKN6z+d//uF+T+VQ3Q7ltJL/71p17ufartARX9Ja5gOGQ==} cpu: [x64] os: [freebsd] - "@rollup/rollup-linux-arm-gnueabihf@4.65.0": - resolution: - { - integrity: sha512-W1vUprf09RWaVWnsFP1vlUkEWJzTZlpObOEAYZXt9Fq0UVAOYEkeGr7wBX7McKqqIQskXdBnpiOMgwPujordgA==, - } + '@rollup/rollup-linux-arm-gnueabihf@4.65.0': + resolution: {integrity: sha512-W1vUprf09RWaVWnsFP1vlUkEWJzTZlpObOEAYZXt9Fq0UVAOYEkeGr7wBX7McKqqIQskXdBnpiOMgwPujordgA==} cpu: [arm] os: [linux] libc: [glibc] - "@rollup/rollup-linux-arm-musleabihf@4.65.0": - resolution: - { - integrity: sha512-s8QTcZWgrl9haFzEGIqyE5BWyo89pFDGLMBFZ752WYxk8DJwNU/O1X6pZgPw829d9/sm6CCHyrMP3aurt76p1Q==, - } + '@rollup/rollup-linux-arm-musleabihf@4.65.0': + resolution: {integrity: sha512-s8QTcZWgrl9haFzEGIqyE5BWyo89pFDGLMBFZ752WYxk8DJwNU/O1X6pZgPw829d9/sm6CCHyrMP3aurt76p1Q==} cpu: [arm] os: [linux] libc: [musl] - "@rollup/rollup-linux-arm64-gnu@4.65.0": - resolution: - { - integrity: sha512-ke1f6dLRzkqveyjjF2fkEwQYZvxHtNNwjegY8nJrwcO0Bdqr8CzFO/pKE0Bns13cx0qz2Ba2Y0Hs3dSwMdnHxA==, - } + '@rollup/rollup-linux-arm64-gnu@4.65.0': + resolution: {integrity: sha512-ke1f6dLRzkqveyjjF2fkEwQYZvxHtNNwjegY8nJrwcO0Bdqr8CzFO/pKE0Bns13cx0qz2Ba2Y0Hs3dSwMdnHxA==} cpu: [arm64] os: [linux] libc: [glibc] - "@rollup/rollup-linux-arm64-musl@4.65.0": - resolution: - { - integrity: sha512-926SMeUcy4y18Rz/w2aJiUKOnyAnm6czg72bXkQEaeemsll+Wr0hnsjb2BGRpHvWgwAMYy5ACJCotbvfLCfj0A==, - } + '@rollup/rollup-linux-arm64-musl@4.65.0': + resolution: {integrity: sha512-926SMeUcy4y18Rz/w2aJiUKOnyAnm6czg72bXkQEaeemsll+Wr0hnsjb2BGRpHvWgwAMYy5ACJCotbvfLCfj0A==} cpu: [arm64] os: [linux] libc: [musl] - "@rollup/rollup-linux-loong64-gnu@4.65.0": - resolution: - { - integrity: sha512-6T7JNRP1hc4hqF2dUsEoVLOt9K8tmODoCLldtQRYnrddGGQY8Gqe2pM/qMMUo7EMowpP7oa2N95k0/8sYK/ipw==, - } + '@rollup/rollup-linux-loong64-gnu@4.65.0': + resolution: {integrity: sha512-6T7JNRP1hc4hqF2dUsEoVLOt9K8tmODoCLldtQRYnrddGGQY8Gqe2pM/qMMUo7EMowpP7oa2N95k0/8sYK/ipw==} cpu: [loong64] os: [linux] libc: [glibc] - "@rollup/rollup-linux-loong64-musl@4.65.0": - resolution: - { - integrity: sha512-SImGFl1Je5kpyAgOCPONVT7h6ASV8mAUwGRjY7eZvVjPg//PfHOXgI+P241RF/+HqsA4PP3qD527ysWFKNQyhQ==, - } + '@rollup/rollup-linux-loong64-musl@4.65.0': + resolution: {integrity: sha512-SImGFl1Je5kpyAgOCPONVT7h6ASV8mAUwGRjY7eZvVjPg//PfHOXgI+P241RF/+HqsA4PP3qD527ysWFKNQyhQ==} cpu: [loong64] os: [linux] libc: [musl] - "@rollup/rollup-linux-ppc64-gnu@4.65.0": - resolution: - { - integrity: sha512-XeDTbmPF1Yu5asfXGPrMkWoxTDhgryQbmw5Q1+R582O51VNCk23J6f2pDrsAp5YkneBVBwtExx6ZGOb9D2aQ6A==, - } + '@rollup/rollup-linux-ppc64-gnu@4.65.0': + resolution: {integrity: sha512-XeDTbmPF1Yu5asfXGPrMkWoxTDhgryQbmw5Q1+R582O51VNCk23J6f2pDrsAp5YkneBVBwtExx6ZGOb9D2aQ6A==} cpu: [ppc64] os: [linux] libc: [glibc] - "@rollup/rollup-linux-ppc64-musl@4.65.0": - resolution: - { - integrity: sha512-VS55JUI/1nsljmV/jfqNkLZXFw8uMO8nNlfoHmSevNf6fO3neuhyDvx9dv3c/Ytt8r/431SL8Qax1dpYvOMZTg==, - } + '@rollup/rollup-linux-ppc64-musl@4.65.0': + resolution: {integrity: sha512-VS55JUI/1nsljmV/jfqNkLZXFw8uMO8nNlfoHmSevNf6fO3neuhyDvx9dv3c/Ytt8r/431SL8Qax1dpYvOMZTg==} cpu: [ppc64] os: [linux] libc: [musl] - "@rollup/rollup-linux-riscv64-gnu@4.65.0": - resolution: - { - integrity: sha512-Vblw84TbX7RS4la1Y4+yQ7aore/oSyaNtFbppuBr5Vs/4LfBeXKq9dIfoExw6zOvRERqd2rRAjnZRaMhH/0x5w==, - } + '@rollup/rollup-linux-riscv64-gnu@4.65.0': + resolution: {integrity: sha512-Vblw84TbX7RS4la1Y4+yQ7aore/oSyaNtFbppuBr5Vs/4LfBeXKq9dIfoExw6zOvRERqd2rRAjnZRaMhH/0x5w==} cpu: [riscv64] os: [linux] libc: [glibc] - "@rollup/rollup-linux-riscv64-musl@4.65.0": - resolution: - { - integrity: sha512-7+gHjtONvKgwR9InTKFaLq53f6YzBJkgeMRBENdWTsQr+spahGqQm8HF5aXpq8i4JaRrOqYyv6Zn1WltDTxaZQ==, - } + '@rollup/rollup-linux-riscv64-musl@4.65.0': + resolution: {integrity: sha512-7+gHjtONvKgwR9InTKFaLq53f6YzBJkgeMRBENdWTsQr+spahGqQm8HF5aXpq8i4JaRrOqYyv6Zn1WltDTxaZQ==} cpu: [riscv64] os: [linux] libc: [musl] - "@rollup/rollup-linux-s390x-gnu@4.65.0": - resolution: - { - integrity: sha512-KvNDC0J6s4vVHhtJ6KrDXU0Er0au/hmRIBq/+1maCV8TseGupeKizq0bWcrbp2HE1T8xoknvN0Q/KZ/YF0qX3w==, - } + '@rollup/rollup-linux-s390x-gnu@4.65.0': + resolution: {integrity: sha512-KvNDC0J6s4vVHhtJ6KrDXU0Er0au/hmRIBq/+1maCV8TseGupeKizq0bWcrbp2HE1T8xoknvN0Q/KZ/YF0qX3w==} cpu: [s390x] os: [linux] libc: [glibc] - "@rollup/rollup-linux-x64-gnu@4.65.0": - resolution: - { - integrity: sha512-GExdD15WPvu/x/drELjVam7vX8EsPgvTMOa/iJL/p/4m/oOqzbb7JXZg8fXR4hlXtg/sF2zyTZ3bR0AEtEMlhQ==, - } + '@rollup/rollup-linux-x64-gnu@4.65.0': + resolution: {integrity: sha512-GExdD15WPvu/x/drELjVam7vX8EsPgvTMOa/iJL/p/4m/oOqzbb7JXZg8fXR4hlXtg/sF2zyTZ3bR0AEtEMlhQ==} cpu: [x64] os: [linux] libc: [glibc] - "@rollup/rollup-linux-x64-musl@4.65.0": - resolution: - { - integrity: sha512-8sT9PemIZfIMED4Vd4KGoOXVDj5CMSWNcGdbheRUx9TYS03yOWdT2Q0lXPyEyNBB9YTilwewIQKNIBk6Nxi0Kg==, - } + '@rollup/rollup-linux-x64-musl@4.65.0': + resolution: {integrity: sha512-8sT9PemIZfIMED4Vd4KGoOXVDj5CMSWNcGdbheRUx9TYS03yOWdT2Q0lXPyEyNBB9YTilwewIQKNIBk6Nxi0Kg==} cpu: [x64] os: [linux] libc: [musl] - "@rollup/rollup-openbsd-x64@4.65.0": - resolution: - { - integrity: sha512-pO6Ehi0jo5yE0R6PltKhJhv9mlIiyzBJk5N5NUF2OF6xtVKI+1bdXxj/rb0jGXy3hck1ELVT0QPY3tzkAm360w==, - } + '@rollup/rollup-openbsd-x64@4.65.0': + resolution: {integrity: sha512-pO6Ehi0jo5yE0R6PltKhJhv9mlIiyzBJk5N5NUF2OF6xtVKI+1bdXxj/rb0jGXy3hck1ELVT0QPY3tzkAm360w==} cpu: [x64] os: [openbsd] - "@rollup/rollup-openharmony-arm64@4.65.0": - resolution: - { - integrity: sha512-vo7N3/+cjjPDYr8vtxC+qSNgWa9sDvJ7g8N8R5SWqFY4WiNH1ULcAU70SKVyHeiIZOYYwNP9pTipmhN5sl1NPw==, - } + '@rollup/rollup-openharmony-arm64@4.65.0': + resolution: {integrity: sha512-vo7N3/+cjjPDYr8vtxC+qSNgWa9sDvJ7g8N8R5SWqFY4WiNH1ULcAU70SKVyHeiIZOYYwNP9pTipmhN5sl1NPw==} cpu: [arm64] os: [openharmony] - "@rollup/rollup-win32-arm64-msvc@4.65.0": - resolution: - { - integrity: sha512-ArwvgFAbjKcDM7qqotZ4OVYf+UMT9xH2YzkCzK4+AuZSDihMlz1Ekoe4INwBVXzn2mnJzuIYOcSx07iTBijGRQ==, - } + '@rollup/rollup-win32-arm64-msvc@4.65.0': + resolution: {integrity: sha512-ArwvgFAbjKcDM7qqotZ4OVYf+UMT9xH2YzkCzK4+AuZSDihMlz1Ekoe4INwBVXzn2mnJzuIYOcSx07iTBijGRQ==} cpu: [arm64] os: [win32] - "@rollup/rollup-win32-ia32-msvc@4.65.0": - resolution: - { - integrity: sha512-QGMmK4/cxKE/36FoLMSHQBCyymv/eFTnsCvvsBF0rgmOvVbvX78VWZRerL3dHa77f4pM6Xcc2iGDhJ10w/svKQ==, - } + '@rollup/rollup-win32-ia32-msvc@4.65.0': + resolution: {integrity: sha512-QGMmK4/cxKE/36FoLMSHQBCyymv/eFTnsCvvsBF0rgmOvVbvX78VWZRerL3dHa77f4pM6Xcc2iGDhJ10w/svKQ==} cpu: [ia32] os: [win32] - "@rollup/rollup-win32-x64-gnu@4.65.0": - resolution: - { - integrity: sha512-XfKYFWHIPs4HPODIMsGF1IKlS3H1e3WokUZIgpmn4+uNnZBlnRDDHEylNyazbQXwhMKZre68OjtRYbKqb5+j8w==, - } + '@rollup/rollup-win32-x64-gnu@4.65.0': + resolution: {integrity: sha512-XfKYFWHIPs4HPODIMsGF1IKlS3H1e3WokUZIgpmn4+uNnZBlnRDDHEylNyazbQXwhMKZre68OjtRYbKqb5+j8w==} cpu: [x64] os: [win32] - "@rollup/rollup-win32-x64-msvc@4.65.0": - resolution: - { - integrity: sha512-wPWi++K4Bs1VR4XihOhfGi4IGX6Cub7FlerTf8X2v+2BtDHddgSqWCXwGD+h2yAe4/niIhNDwAltioOC+wEogQ==, - } + '@rollup/rollup-win32-x64-msvc@4.65.0': + resolution: {integrity: sha512-wPWi++K4Bs1VR4XihOhfGi4IGX6Cub7FlerTf8X2v+2BtDHddgSqWCXwGD+h2yAe4/niIhNDwAltioOC+wEogQ==} cpu: [x64] os: [win32] - "@shikijs/core@3.23.0": - resolution: - { - integrity: sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==, - } - - "@shikijs/engine-javascript@3.23.0": - resolution: - { - integrity: sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==, - } - - "@shikijs/engine-oniguruma@3.23.0": - resolution: - { - integrity: sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==, - } - - "@shikijs/langs@3.23.0": - resolution: - { - integrity: sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==, - } - - "@shikijs/themes@3.23.0": - resolution: - { - integrity: sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==, - } - - "@shikijs/types@3.23.0": - resolution: - { - integrity: sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==, - } - - "@shikijs/vscode-textmate@10.1.0": - resolution: - { - integrity: sha512-cVBuhDQjBZaNMTwBquYfPydrUP+E3hE6oYGasx8h6ZrODIM7/r+gxLDXdd66oszbrggZHEntdE6j+5gYIVyMWQ==, - } - - "@tailwindcss/node@4.3.3": - resolution: - { - integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==, - } - - "@tailwindcss/oxide-android-arm64@4.3.3": - resolution: - { - integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==, - } - engines: { node: ">= 20" } + '@shikijs/core@3.23.0': + resolution: {integrity: sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==} + + '@shikijs/engine-javascript@3.23.0': + resolution: {integrity: sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==} + + '@shikijs/engine-oniguruma@3.23.0': + resolution: {integrity: sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==} + + '@shikijs/langs@3.23.0': + resolution: {integrity: sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==} + + '@shikijs/themes@3.23.0': + resolution: {integrity: sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==} + + '@shikijs/types@3.23.0': + resolution: {integrity: sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==} + + '@shikijs/vscode-textmate@10.1.0': + resolution: {integrity: sha512-cVBuhDQjBZaNMTwBquYfPydrUP+E3hE6oYGasx8h6ZrODIM7/r+gxLDXdd66oszbrggZHEntdE6j+5gYIVyMWQ==} + + '@tailwindcss/node@4.3.3': + resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==} + + '@tailwindcss/oxide-android-arm64@4.3.3': + resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==} + engines: {node: '>= 20'} cpu: [arm64] os: [android] - "@tailwindcss/oxide-darwin-arm64@4.3.3": - resolution: - { - integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-darwin-arm64@4.3.3': + resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==} + engines: {node: '>= 20'} cpu: [arm64] os: [darwin] - "@tailwindcss/oxide-darwin-x64@4.3.3": - resolution: - { - integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-darwin-x64@4.3.3': + resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==} + engines: {node: '>= 20'} cpu: [x64] os: [darwin] - "@tailwindcss/oxide-freebsd-x64@4.3.3": - resolution: - { - integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-freebsd-x64@4.3.3': + resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==} + engines: {node: '>= 20'} cpu: [x64] os: [freebsd] - "@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3": - resolution: - { - integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==} + engines: {node: '>= 20'} cpu: [arm] os: [linux] - "@tailwindcss/oxide-linux-arm64-gnu@4.3.3": - resolution: - { - integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==} + engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [glibc] - "@tailwindcss/oxide-linux-arm64-musl@4.3.3": - resolution: - { - integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==} + engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [musl] - "@tailwindcss/oxide-linux-x64-gnu@4.3.3": - resolution: - { - integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==} + engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [glibc] - "@tailwindcss/oxide-linux-x64-musl@4.3.3": - resolution: - { - integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==} + engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [musl] - "@tailwindcss/oxide-wasm32-wasi@4.3.3": - resolution: - { - integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==, - } - engines: { node: ">=14.0.0" } + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==} + engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: - - "@napi-rs/wasm-runtime" - - "@emnapi/core" - - "@emnapi/runtime" - - "@tybys/wasm-util" - - "@emnapi/wasi-threads" + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' - tslib - "@tailwindcss/oxide-win32-arm64-msvc@4.3.3": - resolution: - { - integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + resolution: {integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==} + engines: {node: '>= 20'} cpu: [arm64] os: [win32] - "@tailwindcss/oxide-win32-x64-msvc@4.3.3": - resolution: - { - integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + resolution: {integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==} + engines: {node: '>= 20'} cpu: [x64] os: [win32] - "@tailwindcss/oxide@4.3.3": - resolution: - { - integrity: sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==, - } - engines: { node: ">= 20" } - - "@tailwindcss/vite@4.3.3": - resolution: - { - integrity: sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==, - } + '@tailwindcss/oxide@4.3.3': + resolution: {integrity: sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==} + engines: {node: '>= 20'} + + '@tailwindcss/vite@4.3.3': + resolution: {integrity: sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==} peerDependencies: vite: ^5.2.0 || ^6 || ^7 || ^8 - "@tybys/wasm-util@0.10.4": - resolution: - { - integrity: sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==, - } - - "@types/babel__core@7.20.5": - resolution: - { - integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==, - } - - "@types/babel__generator@7.27.0": - resolution: - { - integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==, - } - - "@types/babel__template@7.4.4": - resolution: - { - integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==, - } - - "@types/babel__traverse@7.28.0": - resolution: - { - integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==, - } - - "@types/debug@4.1.13": - resolution: - { - integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==, - } - - "@types/estree@1.0.9": - resolution: - { - integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==, - } - - "@types/hast@3.0.5": - resolution: - { - integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==, - } - - "@types/json-schema@7.0.15": - resolution: - { - integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==, - } - - "@types/mdast@4.0.4": - resolution: - { - integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==, - } - - "@types/ms@2.1.0": - resolution: - { - integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==, - } - - "@types/nlcst@2.0.3": - resolution: - { - integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==, - } - - "@types/prop-types@15.7.15": - resolution: - { - integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==, - } - - "@types/react-dom@18.3.7": - resolution: - { - integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==, - } + '@tybys/wasm-util@0.10.4': + resolution: {integrity: sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/hast@3.0.5': + resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/nlcst@2.0.3': + resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} + + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + + '@types/react-dom@18.3.7': + resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} peerDependencies: - "@types/react": ^18.0.0 - - "@types/react@18.3.31": - resolution: - { - integrity: sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==, - } - - "@types/unist@3.0.3": - resolution: - { - integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==, - } - - "@typescript-eslint/eslint-plugin@8.72.0": - resolution: - { - integrity: sha512-aysKgysPX9XOv/dk95UkvUDe++Q3n3dJPG9CEWT3RUccxfkPJ+Qn6nhKPDhZXU6Ofo9kgwGWwKyQCnZIXTgngg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + '@types/react': ^18.0.0 + + '@types/react@18.3.31': + resolution: {integrity: sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@typescript-eslint/eslint-plugin@8.72.0': + resolution: {integrity: sha512-aysKgysPX9XOv/dk95UkvUDe++Q3n3dJPG9CEWT3RUccxfkPJ+Qn6nhKPDhZXU6Ofo9kgwGWwKyQCnZIXTgngg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - "@typescript-eslint/parser": ^8.72.0 + '@typescript-eslint/parser': ^8.72.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: ">=4.8.4 <6.1.0" - - "@typescript-eslint/parser@8.72.0": - resolution: - { - integrity: sha512-0O+x3ynciD6zuXb1CKSu165haQV6ey53DKhHHUnujP7U+XlltJG+/keT0Xhd1HXCze+IHf7Tvt39ovyepX/LNw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.72.0': + resolution: {integrity: sha512-0O+x3ynciD6zuXb1CKSu165haQV6ey53DKhHHUnujP7U+XlltJG+/keT0Xhd1HXCze+IHf7Tvt39ovyepX/LNw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: ">=4.8.4 <6.1.0" - - "@typescript-eslint/project-service@8.72.0": - resolution: - { - integrity: sha512-bzHngBbmIgzj6TsTjVUCntGx6FFLgE7mPjpO0PzHycOaZHXqS43XnrkFIHRS16LLr/xO6FHSXG3GJFo/F4bSBQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.72.0': + resolution: {integrity: sha512-bzHngBbmIgzj6TsTjVUCntGx6FFLgE7mPjpO0PzHycOaZHXqS43XnrkFIHRS16LLr/xO6FHSXG3GJFo/F4bSBQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: ">=4.8.4 <6.1.0" - - "@typescript-eslint/scope-manager@8.72.0": - resolution: - { - integrity: sha512-JewPyJOYcXBSAS7PIgufscHOwZqhTGyBRiQJhhuDln66tMchxvvt7nJ90JKKIoOelmdHA3kAQbm9cEUHc4JSEA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@typescript-eslint/tsconfig-utils@8.72.0": - resolution: - { - integrity: sha512-0vbxCf7xLVPSjcOdm6eQbgViH8EDJ8xnl1laLT9yoqSz2YmcSfSg5Vdl0JUvZf6rcuJ0ZivJIuMD4edTg8obaA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.72.0': + resolution: {integrity: sha512-JewPyJOYcXBSAS7PIgufscHOwZqhTGyBRiQJhhuDln66tMchxvvt7nJ90JKKIoOelmdHA3kAQbm9cEUHc4JSEA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.72.0': + resolution: {integrity: sha512-0vbxCf7xLVPSjcOdm6eQbgViH8EDJ8xnl1laLT9yoqSz2YmcSfSg5Vdl0JUvZf6rcuJ0ZivJIuMD4edTg8obaA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: ">=4.8.4 <6.1.0" - - "@typescript-eslint/type-utils@8.72.0": - resolution: - { - integrity: sha512-gEXxmmbSTSOVTC37IYYvremOM31v+UN2W772bJxmVYPhAOPSUxtacyb7wkm1EhkYKU6KzFJsdU44DCV4to0LWw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.72.0': + resolution: {integrity: sha512-gEXxmmbSTSOVTC37IYYvremOM31v+UN2W772bJxmVYPhAOPSUxtacyb7wkm1EhkYKU6KzFJsdU44DCV4to0LWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: ">=4.8.4 <6.1.0" - - "@typescript-eslint/types@8.72.0": - resolution: - { - integrity: sha512-Adfoy59kxNEjT5E5fnyrp7GpHYuWs8ONbdohu4ez6IU46qDq8LltG90tLmkucZB2uhYL8mqBjtXqGd8yhfjXqQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@typescript-eslint/typescript-estree@8.72.0": - resolution: - { - integrity: sha512-gwLfFgWcw15tN2KXDCJvxEOQjPn+P3j/n3eSqkZ5sX4VpW9Cosodr+tWwBZRvJT3TcKx6HnPRGZMCNAiP1Xhug==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.72.0': + resolution: {integrity: sha512-Adfoy59kxNEjT5E5fnyrp7GpHYuWs8ONbdohu4ez6IU46qDq8LltG90tLmkucZB2uhYL8mqBjtXqGd8yhfjXqQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.72.0': + resolution: {integrity: sha512-gwLfFgWcw15tN2KXDCJvxEOQjPn+P3j/n3eSqkZ5sX4VpW9Cosodr+tWwBZRvJT3TcKx6HnPRGZMCNAiP1Xhug==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: ">=4.8.4 <6.1.0" - - "@typescript-eslint/utils@8.72.0": - resolution: - { - integrity: sha512-0up3oArJZBYnzOaEXKo8DmI43QvMZyPoFEt9LWzRJcyl/RcJjfKKPebknN1VbTgXBf3M/KggUh5uUMdURLnseg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.72.0': + resolution: {integrity: sha512-0up3oArJZBYnzOaEXKo8DmI43QvMZyPoFEt9LWzRJcyl/RcJjfKKPebknN1VbTgXBf3M/KggUh5uUMdURLnseg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: ">=4.8.4 <6.1.0" - - "@typescript-eslint/visitor-keys@8.72.0": - resolution: - { - integrity: sha512-E2mroviasaWecDzTGiGMtuEguuLVOeRggnrIn93CJuxYJzQBHbyymlT08aVQydbBal5538o+JkBKXh2sRuCyIA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@ungap/structured-clone@1.4.0": - resolution: - { - integrity: sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==, - } - - "@vitejs/plugin-react@4.7.0": - resolution: - { - integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==, - } - engines: { node: ^14.18.0 || >=16.0.0 } + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.72.0': + resolution: {integrity: sha512-E2mroviasaWecDzTGiGMtuEguuLVOeRggnrIn93CJuxYJzQBHbyymlT08aVQydbBal5538o+JkBKXh2sRuCyIA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@ungap/structured-clone@1.4.0': + resolution: {integrity: sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==} + + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - "@volar/kit@2.4.30": - resolution: - { - integrity: sha512-VocLtumfXnqnPQEWc9Yx7uC1uOh9gyjVNlrn2+6Zx/z9aM6yTLhPK8lSqM9Ilu81XRa48I00lcYJWLi2BLxx8Q==, - } + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/pretty-format@3.2.7': + resolution: {integrity: sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==} + + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + + '@volar/kit@2.4.30': + resolution: {integrity: sha512-VocLtumfXnqnPQEWc9Yx7uC1uOh9gyjVNlrn2+6Zx/z9aM6yTLhPK8lSqM9Ilu81XRa48I00lcYJWLi2BLxx8Q==} peerDependencies: typescript: <7 - "@volar/language-core@2.4.30": - resolution: - { - integrity: sha512-73fjwEV8wat6aOZ6U/y/hqtE30ocrIWECg67b0txGczOMNw+MffLmvJ5fnFfN9drVUEYZzLdaNo6VAy6G6avMw==, - } - - "@volar/language-server@2.4.30": - resolution: - { - integrity: sha512-tcP9fzzUBbsLIFHhgnRU4ncQjMn9G9Vax0ffkQmdCrXVEYqRroUepmyC5AVgiju5bCiLD6a55qen5iOTQHkb3g==, - } + '@volar/language-core@2.4.30': + resolution: {integrity: sha512-73fjwEV8wat6aOZ6U/y/hqtE30ocrIWECg67b0txGczOMNw+MffLmvJ5fnFfN9drVUEYZzLdaNo6VAy6G6avMw==} + + '@volar/language-server@2.4.30': + resolution: {integrity: sha512-tcP9fzzUBbsLIFHhgnRU4ncQjMn9G9Vax0ffkQmdCrXVEYqRroUepmyC5AVgiju5bCiLD6a55qen5iOTQHkb3g==} peerDependencies: typescript: <7 peerDependenciesMeta: typescript: optional: true - "@volar/language-service@2.4.30": - resolution: - { - integrity: sha512-w/xDsASiqC71ggCOm7sTDXDNk/l72XEptodeRC68tgZ0e5QM4hM3zAqBGC7mBctvhrpYwKfMG+t9xcUo2hEFtA==, - } + '@volar/language-service@2.4.30': + resolution: {integrity: sha512-w/xDsASiqC71ggCOm7sTDXDNk/l72XEptodeRC68tgZ0e5QM4hM3zAqBGC7mBctvhrpYwKfMG+t9xcUo2hEFtA==} peerDependencies: typescript: <7 peerDependenciesMeta: typescript: optional: true - "@volar/source-map@2.4.30": - resolution: - { - integrity: sha512-zym1ob/KwDDyN+0jGVtxwbxJSRRnUvv5HspCtz3yEfVVdCG4/dzxJktMGPAgemqRuVs81O0o/y0q3qOUErR8kg==, - } - - "@volar/typescript@2.4.30": - resolution: - { - integrity: sha512-/DgA5tgYFPfrLFuSjIMXMGHk1LQv2+P8eny6+BhGNzFJELWIW7UZlc56ZLIRFhLsPpMfIFxM8/HvOPBggrjTZg==, - } + '@volar/source-map@2.4.30': + resolution: {integrity: sha512-zym1ob/KwDDyN+0jGVtxwbxJSRRnUvv5HspCtz3yEfVVdCG4/dzxJktMGPAgemqRuVs81O0o/y0q3qOUErR8kg==} + + '@volar/typescript@2.4.30': + resolution: {integrity: sha512-/DgA5tgYFPfrLFuSjIMXMGHk1LQv2+P8eny6+BhGNzFJELWIW7UZlc56ZLIRFhLsPpMfIFxM8/HvOPBggrjTZg==} peerDependencies: typescript: <7 peerDependenciesMeta: typescript: optional: true - "@vscode/emmet-helper@2.11.0": - resolution: - { - integrity: sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw==, - } + '@vscode/emmet-helper@2.11.0': + resolution: {integrity: sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw==} - "@vscode/l10n@0.0.18": - resolution: - { - integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==, - } + '@vscode/l10n@0.0.18': + resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==} acorn-jsx@5.3.2: - resolution: - { - integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, - } + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 acorn@8.19.0: - resolution: - { - integrity: sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==, - } - engines: { node: ">=0.4.0" } + resolution: {integrity: sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==} + engines: {node: '>=0.4.0'} hasBin: true ajv-draft-04@1.0.0: - resolution: - { - integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==, - } + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} peerDependencies: ajv: ^8.5.0 peerDependenciesMeta: @@ -1930,925 +1297,544 @@ packages: optional: true ajv-i18n@4.2.0: - resolution: - { - integrity: sha512-v/ei2UkCEeuKNXh8RToiFsUclmU+G57LO1Oo22OagNMENIw+Yb8eMwvHu7Vn9fmkjJyv6XclhJ8TbuigSglPkg==, - } + resolution: {integrity: sha512-v/ei2UkCEeuKNXh8RToiFsUclmU+G57LO1Oo22OagNMENIw+Yb8eMwvHu7Vn9fmkjJyv6XclhJ8TbuigSglPkg==} peerDependencies: ajv: ^8.0.0-beta.0 ajv@6.15.0: - resolution: - { - integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==, - } + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} ajv@8.20.0: - resolution: - { - integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==, - } + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} ansi-align@3.0.1: - resolution: - { - integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==, - } + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} ansi-regex@5.0.1: - resolution: - { - integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} ansi-regex@6.4.0: - resolution: - { - integrity: sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==} + engines: {node: '>=12'} ansi-styles@4.3.0: - resolution: - { - integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} ansi-styles@6.2.3: - resolution: - { - integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} anymatch@3.1.3: - resolution: - { - integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==, - } - engines: { node: ">= 8" } + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} argparse@2.0.1: - resolution: - { - integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==, - } + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} aria-query@5.3.2: - resolution: - { - integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} array-buffer-byte-length@1.0.2: - resolution: - { - integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} array-includes@3.2.0: - resolution: - { - integrity: sha512-VXY5eFRarnXcYxwBjJzPmEhH55+rmP79/+ueDhi0F+TuqfHCItagIHqxeUZrmgrOPa31QTh9H85DjX3FfJ0FTg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-VXY5eFRarnXcYxwBjJzPmEhH55+rmP79/+ueDhi0F+TuqfHCItagIHqxeUZrmgrOPa31QTh9H85DjX3FfJ0FTg==} + engines: {node: '>= 0.4'} array-iterate@2.0.1: - resolution: - { - integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==, - } + resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} array.prototype.findlast@1.2.5: - resolution: - { - integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} array.prototype.flat@1.3.3: - resolution: - { - integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} array.prototype.flatmap@1.3.3: - resolution: - { - integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} array.prototype.tosorted@1.1.4: - resolution: - { - integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} arraybuffer.prototype.slice@1.0.4: - resolution: - { - integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} astro@5.18.2: - resolution: - { - integrity: sha512-TnFwLnAXty5MXKPDGuKXqK4AMBXG+FH6RUdK7Oyc3gyfNoFIthT+4eRbzOK43bdRlLaZuxgciDSjgtggZ3OtGQ==, - } - engines: { node: 18.20.8 || ^20.3.0 || >=22.0.0, npm: ">=9.6.5", pnpm: ">=7.1.0" } + resolution: {integrity: sha512-TnFwLnAXty5MXKPDGuKXqK4AMBXG+FH6RUdK7Oyc3gyfNoFIthT+4eRbzOK43bdRlLaZuxgciDSjgtggZ3OtGQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'} hasBin: true async-function@1.0.0: - resolution: - { - integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} available-typed-arrays@1.0.7: - resolution: - { - integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} axobject-query@4.1.0: - resolution: - { - integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} bail@2.0.2: - resolution: - { - integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==, - } + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} balanced-match@1.0.2: - resolution: - { - integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, - } + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} balanced-match@4.0.4: - resolution: - { - integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==, - } - engines: { node: 18 || 20 || >=22 } + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} base-64@1.0.0: - resolution: - { - integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==, - } + resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} baseline-browser-mapping@2.11.31: - resolution: - { - integrity: sha512-b0k7wXlWjpuL/Uxb/pYlpjY0ej5BjAmurtPUk3eRGuloI4yjK32m8TiKUQ81l1ptcLjKS/KH2+uqIolZti1x4Q==, - } - engines: { node: ">=6.0.0" } + resolution: {integrity: sha512-b0k7wXlWjpuL/Uxb/pYlpjY0ej5BjAmurtPUk3eRGuloI4yjK32m8TiKUQ81l1ptcLjKS/KH2+uqIolZti1x4Q==} + engines: {node: '>=6.0.0'} hasBin: true boolbase@1.0.0: - resolution: - { - integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==, - } + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} boxen@8.0.1: - resolution: - { - integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} + engines: {node: '>=18'} brace-expansion@1.1.21: - resolution: - { - integrity: sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==, - } + resolution: {integrity: sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==} brace-expansion@5.0.12: - resolution: - { - integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==, - } - engines: { node: 20 || >=22 } + resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} + engines: {node: 20 || >=22} browserslist@4.29.4: - resolution: - { - integrity: sha512-2TcbbiwXEq+N0kD36tykpCfyyQigUXmuwNQbnPp4Xk4PX9JOqVIKjJlIDjpH7uY6tmRGoAd+apeT1p60T8wsdA==, - } - engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } + resolution: {integrity: sha512-2TcbbiwXEq+N0kD36tykpCfyyQigUXmuwNQbnPp4Xk4PX9JOqVIKjJlIDjpH7uY6tmRGoAd+apeT1p60T8wsdA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + call-bind-apply-helpers@1.0.2: - resolution: - { - integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} call-bind@1.0.9: - resolution: - { - integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} + engines: {node: '>= 0.4'} call-bound@1.0.4: - resolution: - { - integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} callsites@3.1.0: - resolution: - { - integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} camelcase@8.0.0: - resolution: - { - integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==, - } - engines: { node: ">=16" } + resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} + engines: {node: '>=16'} caniuse-lite@1.0.30001819: - resolution: - { - integrity: sha512-LFUqIIAwpzsIuevnKnsR9qnStBcgzPShTX61lBwN9uFkkf8o8zLiebEjbW8AYtZiF+1YKDWfdt/pWDtMuG8Xwg==, - } + resolution: {integrity: sha512-LFUqIIAwpzsIuevnKnsR9qnStBcgzPShTX61lBwN9uFkkf8o8zLiebEjbW8AYtZiF+1YKDWfdt/pWDtMuG8Xwg==} ccount@2.0.1: - resolution: - { - integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==, - } + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} chalk@4.1.2: - resolution: - { - integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} chalk@5.6.2: - resolution: - { - integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==, - } - engines: { node: ^12.17.0 || ^14.13 || >=16.0.0 } + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} character-entities-html4@2.1.0: - resolution: - { - integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==, - } + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} character-entities-legacy@3.0.0: - resolution: - { - integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==, - } + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} character-entities@2.0.2: - resolution: - { - integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==, - } + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} chokidar@5.0.0: - resolution: - { - integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==, - } - engines: { node: ">= 20.19.0" } + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} ci-info@4.4.0: - resolution: - { - integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} + engines: {node: '>=8'} cli-boxes@3.0.0: - resolution: - { - integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} cliui@9.0.1: - resolution: - { - integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==, - } - engines: { node: ">=20" } + resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} + engines: {node: '>=20'} clsx@2.1.1: - resolution: - { - integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} color-convert@2.0.1: - resolution: - { - integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, - } - engines: { node: ">=7.0.0" } + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} color-name@1.1.4: - resolution: - { - integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, - } + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} comma-separated-tokens@2.0.3: - resolution: - { - integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==, - } + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} commander@11.1.0: - resolution: - { - integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==, - } - engines: { node: ">=16" } + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} common-ancestor-path@1.0.1: - resolution: - { - integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==, - } + resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} concat-map@0.0.1: - resolution: - { - integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==, - } + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} convert-source-map@2.0.0: - resolution: - { - integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, - } + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} cookie-es@1.2.3: - resolution: - { - integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==, - } + resolution: {integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==} cookie@1.1.1: - resolution: - { - integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} cross-spawn@7.0.6: - resolution: - { - integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, - } - engines: { node: ">= 8" } + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} crossws@0.3.5: - resolution: - { - integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==, - } + resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} css-select@6.0.0: - resolution: - { - integrity: sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==, - } + resolution: {integrity: sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==} css-tree@2.2.1: - resolution: - { - integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==, - } - engines: { node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: ">=7.0.0" } + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} css-tree@3.2.1: - resolution: - { - integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==, - } - engines: { node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0 } + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} css-what@7.0.0: - resolution: - { - integrity: sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==, - } - engines: { node: ">= 6" } + resolution: {integrity: sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==} + engines: {node: '>= 6'} cssesc@3.0.0: - resolution: - { - integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} hasBin: true csso@5.0.5: - resolution: - { - integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==, - } - engines: { node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: ">=7.0.0" } + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} csstype@3.2.3: - resolution: - { - integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==, - } + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} data-view-buffer@1.0.2: - resolution: - { - integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} data-view-byte-length@1.0.2: - resolution: - { - integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} data-view-byte-offset@1.0.1: - resolution: - { - integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} debug@4.4.3: - resolution: - { - integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, - } - engines: { node: ">=6.0" } + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} peerDependencies: - supports-color: "*" + supports-color: '*' peerDependenciesMeta: supports-color: optional: true decode-named-character-reference@1.3.0: - resolution: - { - integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==, - } + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} deep-is@0.1.4: - resolution: - { - integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, - } + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} define-data-property@1.1.4: - resolution: - { - integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} define-properties@1.2.1: - resolution: - { - integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} defu@6.1.7: - resolution: - { - integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==, - } + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} dequal@2.0.3: - resolution: - { - integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} destr@2.0.5: - resolution: - { - integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==, - } + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} detect-libc@2.1.2: - resolution: - { - integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} deterministic-object-hash@2.0.2: - resolution: - { - integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==} + engines: {node: '>=18'} devalue@5.9.4: - resolution: - { - integrity: sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg==, - } + resolution: {integrity: sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg==} devlop@1.1.0: - resolution: - { - integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==, - } + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} diff@8.0.4: - resolution: - { - integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==, - } - engines: { node: ">=0.3.1" } + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} + engines: {node: '>=0.3.1'} dlv@1.1.3: - resolution: - { - integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==, - } + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} doctrine@2.1.0: - resolution: - { - integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} dom-serializer@2.0.0: - resolution: - { - integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==, - } + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} domelementtype@2.3.0: - resolution: - { - integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==, - } + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} domhandler@5.0.3: - resolution: - { - integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==, - } - engines: { node: ">= 4" } + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} domutils@3.2.2: - resolution: - { - integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==, - } + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} dset@3.1.4: - resolution: - { - integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} + engines: {node: '>=4'} dunder-proto@1.0.1: - resolution: - { - integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} electron-to-chromium@1.5.463: - resolution: - { - integrity: sha512-nVWNU6ltFjopvOdD44J4zoBkgO0M+WZiFKKFV7EjebOH9z0KNphk6bCEjplXtJofL21ARIuuwWOCsFkBVklm6w==, - } + resolution: {integrity: sha512-nVWNU6ltFjopvOdD44J4zoBkgO0M+WZiFKKFV7EjebOH9z0KNphk6bCEjplXtJofL21ARIuuwWOCsFkBVklm6w==} emmet@2.4.11: - resolution: - { - integrity: sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==, - } + resolution: {integrity: sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==} emoji-regex@10.6.0: - resolution: - { - integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==, - } + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} emoji-regex@8.0.0: - resolution: - { - integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==, - } + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} enhanced-resolve@5.26.0: - resolution: - { - integrity: sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==, - } - engines: { node: ">=10.13.0" } + resolution: {integrity: sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==} + engines: {node: '>=10.13.0'} entities@4.5.0: - resolution: - { - integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==, - } - engines: { node: ">=0.12" } + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} entities@6.0.1: - resolution: - { - integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==, - } - engines: { node: ">=0.12" } + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} es-abstract-get@1.0.0: - resolution: - { - integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==} + engines: {node: '>= 0.4'} es-abstract@1.24.2: - resolution: - { - integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} + engines: {node: '>= 0.4'} es-define-property@1.0.1: - resolution: - { - integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} es-errors@1.3.0: - resolution: - { - integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} es-iterator-helpers@1.4.0: - resolution: - { - integrity: sha512-c/A0P0oxkACDc+cKWw8evLXK83oBKgn0qPOqCYT4x9uolpCIJAcYvJC9QYKNDRPsTeGyCrQ326jrvgZWdCdK5Q==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-c/A0P0oxkACDc+cKWw8evLXK83oBKgn0qPOqCYT4x9uolpCIJAcYvJC9QYKNDRPsTeGyCrQ326jrvgZWdCdK5Q==} + engines: {node: '>= 0.4'} es-module-lexer@1.7.0: - resolution: - { - integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==, - } + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} es-object-atoms@1.1.2: - resolution: - { - integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} es-set-tostringtag@2.1.0: - resolution: - { - integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} es-shim-unscopables@1.1.0: - resolution: - { - integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} es-to-primitive@1.3.4: - resolution: - { - integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==} + engines: {node: '>= 0.4'} esbuild@0.25.12: - resolution: - { - integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} hasBin: true esbuild@0.27.7: - resolution: - { - integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + engines: {node: '>=18'} hasBin: true escalade@3.2.0: - resolution: - { - integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} escape-string-regexp@4.0.0: - resolution: - { - integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} escape-string-regexp@5.0.0: - resolution: - { - integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} eslint-plugin-react-hooks@5.2.0: - resolution: - { - integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 eslint-plugin-react@7.37.5: - resolution: - { - integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 eslint-scope@8.4.0: - resolution: - { - integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: - resolution: - { - integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} eslint-visitor-keys@4.2.1: - resolution: - { - integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@5.0.1: - resolution: - { - integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==, - } - engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} eslint@9.39.5: - resolution: - { - integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true peerDependencies: - jiti: "*" + jiti: '*' peerDependenciesMeta: jiti: optional: true espree@10.4.0: - resolution: - { - integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} esquery@1.7.0: - resolution: - { - integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==, - } - engines: { node: ">=0.10" } + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} esrecurse@4.3.0: - resolution: - { - integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==, - } - engines: { node: ">=4.0" } + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} estraverse@5.3.0: - resolution: - { - integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==, - } - engines: { node: ">=4.0" } + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} estree-walker@2.0.2: - resolution: - { - integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==, - } + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} estree-walker@3.0.3: - resolution: - { - integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==, - } + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} esutils@2.0.3: - resolution: - { - integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} eventemitter3@5.0.4: - resolution: - { - integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==, - } + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} extend@3.0.2: - resolution: - { - integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==, - } + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} fast-deep-equal@3.1.3: - resolution: - { - integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, - } + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} fast-json-stable-stringify@2.1.0: - resolution: - { - integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==, - } + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} fast-levenshtein@2.0.6: - resolution: - { - integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, - } + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} fast-uri@3.1.9: - resolution: - { - integrity: sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==, - } + resolution: {integrity: sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==} fdir@6.5.0: - resolution: - { - integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, - } - engines: { node: ">=12.0.0" } + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -2856,1494 +1842,868 @@ packages: optional: true file-entry-cache@8.0.0: - resolution: - { - integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==, - } - engines: { node: ">=16.0.0" } + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} find-up@5.0.0: - resolution: - { - integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} flat-cache@4.0.1: - resolution: - { - integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==, - } - engines: { node: ">=16" } + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} flatted@3.4.4: - resolution: - { - integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==, - } + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} flattie@1.1.1: - resolution: - { - integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==} + engines: {node: '>=8'} fontace@0.4.1: - resolution: - { - integrity: sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==, - } + resolution: {integrity: sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==} fontkitten@1.0.3: - resolution: - { - integrity: sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==, - } - engines: { node: ">=20" } + resolution: {integrity: sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==} + engines: {node: '>=20'} for-each@0.3.5: - resolution: - { - integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] fsevents@2.3.3: - resolution: - { - integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, - } - engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] function-bind@1.1.2: - resolution: - { - integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==, - } + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} function.prototype.name@1.2.0: - resolution: - { - integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==} + engines: {node: '>= 0.4'} functions-have-names@1.2.3: - resolution: - { - integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==, - } + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} generator-function@2.0.1: - resolution: - { - integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} gensync@1.0.0-beta.2: - resolution: - { - integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==, - } - engines: { node: ">=6.9.0" } + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} get-caller-file@2.0.5: - resolution: - { - integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==, - } - engines: { node: 6.* || 8.* || >= 10.* } + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} get-east-asian-width@1.7.0: - resolution: - { - integrity: sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==} + engines: {node: '>=18'} get-intrinsic@1.3.0: - resolution: - { - integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} get-proto@1.0.1: - resolution: - { - integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} get-symbol-description@1.1.0: - resolution: - { - integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} github-slugger@2.0.0: - resolution: - { - integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==, - } + resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} glob-parent@6.0.2: - resolution: - { - integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==, - } - engines: { node: ">=10.13.0" } + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} globals@14.0.0: - resolution: - { - integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} globals@17.13.0: - resolution: - { - integrity: sha512-RwMTC61u7hrG3ZJMkZQOK4JLJrKS8QtoTii63EmWvFXHqycY9FObBJQCbsLxSO8kjKhWE5kV1GC+Vb1g3RI0Zg==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-RwMTC61u7hrG3ZJMkZQOK4JLJrKS8QtoTii63EmWvFXHqycY9FObBJQCbsLxSO8kjKhWE5kV1GC+Vb1g3RI0Zg==} + engines: {node: '>=18'} globalthis@1.0.4: - resolution: - { - integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} gopd@1.2.0: - resolution: - { - integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} graceful-fs@4.2.11: - resolution: - { - integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, - } + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} h3@1.15.11: - resolution: - { - integrity: sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==, - } + resolution: {integrity: sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==} has-bigints@1.1.0: - resolution: - { - integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} has-flag@4.0.0: - resolution: - { - integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} has-property-descriptors@1.0.2: - resolution: - { - integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==, - } + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} has-proto@1.2.0: - resolution: - { - integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} has-symbols@1.1.0: - resolution: - { - integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} has-tostringtag@1.0.2: - resolution: - { - integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} hasown@2.0.4: - resolution: - { - integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} hast-util-from-html@2.0.3: - resolution: - { - integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==, - } + resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} hast-util-from-parse5@8.0.3: - resolution: - { - integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==, - } + resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} hast-util-is-element@3.0.0: - resolution: - { - integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==, - } + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} hast-util-parse-selector@4.0.0: - resolution: - { - integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==, - } + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} hast-util-raw@9.1.0: - resolution: - { - integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==, - } + resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} hast-util-to-html@9.0.5: - resolution: - { - integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==, - } + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} hast-util-to-parse5@8.0.1: - resolution: - { - integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==, - } + resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==} hast-util-to-text@4.0.2: - resolution: - { - integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==, - } + resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} hast-util-whitespace@3.0.0: - resolution: - { - integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==, - } + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} hastscript@9.0.1: - resolution: - { - integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==, - } + resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} html-escaper@3.0.3: - resolution: - { - integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==, - } + resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} html-void-elements@3.0.0: - resolution: - { - integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==, - } + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} http-cache-semantics@4.3.0: - resolution: - { - integrity: sha512-M5t5LlJpS1UHMjvwRQVdFHvPISGeLAxNcrWuJkeGh0KxsqCHZ1O3NXZU/8x7cD0BDcGW8kapxMKTvwlqrNkHkA==, - } + resolution: {integrity: sha512-M5t5LlJpS1UHMjvwRQVdFHvPISGeLAxNcrWuJkeGh0KxsqCHZ1O3NXZU/8x7cD0BDcGW8kapxMKTvwlqrNkHkA==} ignore@5.3.2: - resolution: - { - integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==, - } - engines: { node: ">= 4" } + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} ignore@7.0.13: - resolution: - { - integrity: sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q==, - } - engines: { node: ">= 4" } + resolution: {integrity: sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q==} + engines: {node: '>= 4'} import-fresh@3.3.1: - resolution: - { - integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} import-meta-resolve@4.2.0: - resolution: - { - integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==, - } + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} imurmurhash@0.1.4: - resolution: - { - integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, - } - engines: { node: ">=0.8.19" } + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} internal-slot@1.1.0: - resolution: - { - integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} iron-webcrypto@1.2.1: - resolution: - { - integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==, - } + resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} is-array-buffer@3.0.5: - resolution: - { - integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} is-async-function@2.1.1: - resolution: - { - integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} is-bigint@1.1.0: - resolution: - { - integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} is-boolean-object@1.2.2: - resolution: - { - integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} is-callable@1.2.7: - resolution: - { - integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} is-core-module@2.17.0: - resolution: - { - integrity: sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA==} + engines: {node: '>= 0.4'} is-data-view@1.0.2: - resolution: - { - integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} is-date-object@1.1.0: - resolution: - { - integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} is-docker@3.0.0: - resolution: - { - integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==, - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} hasBin: true is-document.all@1.0.0: - resolution: - { - integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==} + engines: {node: '>= 0.4'} is-extglob@2.1.1: - resolution: - { - integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} is-finalizationregistry@1.1.1: - resolution: - { - integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} is-fullwidth-code-point@3.0.0: - resolution: - { - integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} is-generator-function@1.1.2: - resolution: - { - integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} is-glob@4.0.3: - resolution: - { - integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} is-inside-container@1.0.0: - resolution: - { - integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==, - } - engines: { node: ">=14.16" } + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} hasBin: true is-map@2.0.3: - resolution: - { - integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} is-negative-zero@2.0.3: - resolution: - { - integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} is-number-object@1.1.1: - resolution: - { - integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} is-plain-obj@4.1.0: - resolution: - { - integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} is-regex@1.2.1: - resolution: - { - integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} is-set@2.0.3: - resolution: - { - integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} is-shared-array-buffer@1.0.4: - resolution: - { - integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} is-string@1.1.1: - resolution: - { - integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} is-symbol@1.1.1: - resolution: - { - integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} is-typed-array@1.1.15: - resolution: - { - integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} is-weakmap@2.0.2: - resolution: - { - integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} is-weakref@1.1.1: - resolution: - { - integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} is-weakset@2.0.4: - resolution: - { - integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} is-wsl@3.1.1: - resolution: - { - integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==, - } - engines: { node: ">=16" } + resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} + engines: {node: '>=16'} isarray@2.0.5: - resolution: - { - integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==, - } + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} isexe@2.0.0: - resolution: - { - integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, - } + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} iterator.prototype@1.1.5: - resolution: - { - integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} jiti@2.7.0: - resolution: - { - integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==, - } + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true js-tokens@4.0.0: - resolution: - { - integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, - } + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} js-yaml@4.3.2: - resolution: - { - integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==, - } + resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} hasBin: true jsesc@3.1.0: - resolution: - { - integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} hasBin: true json-buffer@3.0.1: - resolution: - { - integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==, - } + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} json-schema-traverse@0.4.1: - resolution: - { - integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==, - } + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} json-schema-traverse@1.0.0: - resolution: - { - integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==, - } + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} json-stable-stringify-without-jsonify@1.0.1: - resolution: - { - integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==, - } + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} json5@2.2.3: - resolution: - { - integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} hasBin: true jsonc-parser@2.3.1: - resolution: - { - integrity: sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==, - } + resolution: {integrity: sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==} jsonc-parser@3.3.1: - resolution: - { - integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==, - } + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} jsx-ast-utils@3.3.5: - resolution: - { - integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==, - } - engines: { node: ">=4.0" } + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} keyv@4.5.4: - resolution: - { - integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==, - } + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} kleur@3.0.3: - resolution: - { - integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} kleur@4.1.5: - resolution: - { - integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} levn@0.4.1: - resolution: - { - integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==, - } - engines: { node: ">= 0.8.0" } + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} lightningcss-android-arm64@1.32.0: - resolution: - { - integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] lightningcss-darwin-arm64@1.32.0: - resolution: - { - integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] lightningcss-darwin-x64@1.32.0: - resolution: - { - integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] lightningcss-freebsd-x64@1.32.0: - resolution: - { - integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] lightningcss-linux-arm-gnueabihf@1.32.0: - resolution: - { - integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] lightningcss-linux-arm64-gnu@1.32.0: - resolution: - { - integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: - resolution: - { - integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [musl] lightningcss-linux-x64-gnu@1.32.0: - resolution: - { - integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [glibc] lightningcss-linux-x64-musl@1.32.0: - resolution: - { - integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: - resolution: - { - integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] lightningcss-win32-x64-msvc@1.32.0: - resolution: - { - integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] lightningcss@1.32.0: - resolution: - { - integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} locate-path@6.0.0: - resolution: - { - integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} lodash.merge@4.6.2: - resolution: - { - integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==, - } + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} longest-streak@3.1.0: - resolution: - { - integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==, - } + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} loose-envify@1.4.0: - resolution: - { - integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==, - } + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + lru-cache@11.5.3: - resolution: - { - integrity: sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==, - } - engines: { node: 20 || >=22 } + resolution: {integrity: sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==} + engines: {node: 20 || >=22} lru-cache@5.1.1: - resolution: - { - integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==, - } + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} magic-string@0.30.21: - resolution: - { - integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==, - } + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} magicast@0.5.5: - resolution: - { - integrity: sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==, - } + resolution: {integrity: sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==} markdown-table@3.0.4: - resolution: - { - integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==, - } + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} math-intrinsics@1.1.0: - resolution: - { - integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} mdast-util-definitions@6.0.0: - resolution: - { - integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==, - } + resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==} mdast-util-find-and-replace@3.0.3: - resolution: - { - integrity: sha512-xPgpDNl0/OXHsI7TlaIs22lWnH3KlpvZdXG1ET/m/YT2Hhdkx8lJV4kLphE6l9simyM4KPDFBwas08BgEob6Jw==, - } + resolution: {integrity: sha512-xPgpDNl0/OXHsI7TlaIs22lWnH3KlpvZdXG1ET/m/YT2Hhdkx8lJV4kLphE6l9simyM4KPDFBwas08BgEob6Jw==} mdast-util-from-markdown@2.1.0: - resolution: - { - integrity: sha512-KRLgmPaP2H1ul2bpoJkOS+9pAcDAAj9Nu1mqLnh8NLeJG5tlvj42TH80PIGBpROWlRXLk0lDrWNuZCFy/tsB4A==, - } + resolution: {integrity: sha512-KRLgmPaP2H1ul2bpoJkOS+9pAcDAAj9Nu1mqLnh8NLeJG5tlvj42TH80PIGBpROWlRXLk0lDrWNuZCFy/tsB4A==} mdast-util-gfm-autolink-literal@2.0.1: - resolution: - { - integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==, - } + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} mdast-util-gfm-footnote@2.1.0: - resolution: - { - integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==, - } + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} mdast-util-gfm-strikethrough@2.0.1: - resolution: - { - integrity: sha512-OuJHqvr455pwu2OaOrir7dbzqs4jlWKOtlu9L6GjcIjjQwNyw02VntQsr/Ek6/A13vgnXfUhBekWRUP1OkNivw==, - } + resolution: {integrity: sha512-OuJHqvr455pwu2OaOrir7dbzqs4jlWKOtlu9L6GjcIjjQwNyw02VntQsr/Ek6/A13vgnXfUhBekWRUP1OkNivw==} mdast-util-gfm-table@2.0.0: - resolution: - { - integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==, - } + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} mdast-util-gfm-task-list-item@2.0.0: - resolution: - { - integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==, - } + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} mdast-util-gfm@3.1.0: - resolution: - { - integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==, - } + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} mdast-util-phrasing@4.1.0: - resolution: - { - integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==, - } + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} mdast-util-to-hast@13.2.1: - resolution: - { - integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==, - } + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} mdast-util-to-markdown@2.2.0: - resolution: - { - integrity: sha512-2Jn/ADrHFFyyniBMi0bncTbJqU78K3/TEYvzvwrWWzD9MYPUa/Gm4r3nOeKFQg/PDM5BsbDYdc8ackW4NTY6NA==, - } + resolution: {integrity: sha512-2Jn/ADrHFFyyniBMi0bncTbJqU78K3/TEYvzvwrWWzD9MYPUa/Gm4r3nOeKFQg/PDM5BsbDYdc8ackW4NTY6NA==} mdast-util-to-string@4.0.0: - resolution: - { - integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==, - } + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} mdn-data@2.0.28: - resolution: - { - integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==, - } + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} mdn-data@2.27.1: - resolution: - { - integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==, - } + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} micromark-core-commonmark@2.0.4: - resolution: - { - integrity: sha512-wxEeE8v8XVvOrxn1TZj74qYhAtTQsSqufHVS3uNVyT1MupXxhyaHk8/9xDNPh9BmL77QNDgWTkZ3RsYHB2ry7w==, - } + resolution: {integrity: sha512-wxEeE8v8XVvOrxn1TZj74qYhAtTQsSqufHVS3uNVyT1MupXxhyaHk8/9xDNPh9BmL77QNDgWTkZ3RsYHB2ry7w==} micromark-extension-gfm-autolink-literal@2.1.0: - resolution: - { - integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==, - } + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} micromark-extension-gfm-footnote@2.1.0: - resolution: - { - integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==, - } + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} micromark-extension-gfm-strikethrough@2.1.0: - resolution: - { - integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==, - } + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} micromark-extension-gfm-table@2.1.2: - resolution: - { - integrity: sha512-pRzm4kDTu0MjlmBkxmS9yYhw60nncfcEwu9NNdPFSQEFXS95ZKyIIyTSHu/o3ReBUrLKYEq+7YaXCRn/bPB4MA==, - } + resolution: {integrity: sha512-pRzm4kDTu0MjlmBkxmS9yYhw60nncfcEwu9NNdPFSQEFXS95ZKyIIyTSHu/o3ReBUrLKYEq+7YaXCRn/bPB4MA==} micromark-extension-gfm-tagfilter@2.0.0: - resolution: - { - integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==, - } + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} micromark-extension-gfm-task-list-item@2.1.0: - resolution: - { - integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==, - } + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} micromark-extension-gfm@3.0.0: - resolution: - { - integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==, - } + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} micromark-factory-destination@2.0.1: - resolution: - { - integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==, - } + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} micromark-factory-label@2.0.1: - resolution: - { - integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==, - } + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} micromark-factory-space@2.1.0: - resolution: - { - integrity: sha512-fS8hnLIjnjvdQIj39Geug8wWsR0HrYZ43KShKxNfwT7t2LOHo/LbWZEzEaSOjwtjdCsjoE6syHhonEzW0zv0+Q==, - } + resolution: {integrity: sha512-fS8hnLIjnjvdQIj39Geug8wWsR0HrYZ43KShKxNfwT7t2LOHo/LbWZEzEaSOjwtjdCsjoE6syHhonEzW0zv0+Q==} micromark-factory-title@2.0.1: - resolution: - { - integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==, - } + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} micromark-factory-whitespace@2.0.1: - resolution: - { - integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==, - } + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} micromark-util-character@2.1.1: - resolution: - { - integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==, - } + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} micromark-util-chunked@2.0.1: - resolution: - { - integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==, - } + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} micromark-util-classify-character@2.0.1: - resolution: - { - integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==, - } + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} micromark-util-combine-extensions@2.0.1: - resolution: - { - integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==, - } + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} micromark-util-decode-numeric-character-reference@2.0.2: - resolution: - { - integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==, - } + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} micromark-util-decode-string@2.0.1: - resolution: - { - integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==, - } + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} micromark-util-edit-map@1.0.0: - resolution: - { - integrity: sha512-Pa2ljlsEL6sVwFaYeyrOLSYbQt73JvGbPOYFq+9AElXiSnfI5Q4465RRZ1sHv7lDjDOnGRDDaqPXqClqWK/q1Q==, - } + resolution: {integrity: sha512-Pa2ljlsEL6sVwFaYeyrOLSYbQt73JvGbPOYFq+9AElXiSnfI5Q4465RRZ1sHv7lDjDOnGRDDaqPXqClqWK/q1Q==} micromark-util-encode@2.0.1: - resolution: - { - integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==, - } + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} micromark-util-html-tag-name@2.0.1: - resolution: - { - integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==, - } + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} micromark-util-normalize-identifier@2.0.1: - resolution: - { - integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==, - } + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} micromark-util-resolve-all@2.0.1: - resolution: - { - integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==, - } + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} micromark-util-sanitize-uri@2.0.1: - resolution: - { - integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==, - } + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} micromark-util-subtokenize@2.1.0: - resolution: - { - integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==, - } + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} micromark-util-symbol@2.0.1: - resolution: - { - integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==, - } + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} micromark-util-types@2.0.3: - resolution: - { - integrity: sha512-oxB2Ik03hI0gv+VNn9tnh1t1YEe9MDPptViAEgfdf3YQHsn0pzGTgCdlSCJXcwhqm8phaEuM7zeEu3QQzVBrPg==, - } + resolution: {integrity: sha512-oxB2Ik03hI0gv+VNn9tnh1t1YEe9MDPptViAEgfdf3YQHsn0pzGTgCdlSCJXcwhqm8phaEuM7zeEu3QQzVBrPg==} micromark@4.0.3: - resolution: - { - integrity: sha512-oGYfQzHSG5dOMovQcJ3fyTmZlWAWpi0XA0sJwJs+i6OT88o1+Jtw/8z0CmdowSLxGhhFK89rQ/oXph/wN02PNw==, - } + resolution: {integrity: sha512-oGYfQzHSG5dOMovQcJ3fyTmZlWAWpi0XA0sJwJs+i6OT88o1+Jtw/8z0CmdowSLxGhhFK89rQ/oXph/wN02PNw==} minimatch@10.2.6: - resolution: - { - integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==, - } - engines: { node: 18 || 20 || >=22 } + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} minimatch@3.1.5: - resolution: - { - integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==, - } + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} mrmime@2.0.1: - resolution: - { - integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} ms@2.1.3: - resolution: - { - integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, - } + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} muggle-string@0.4.1: - resolution: - { - integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==, - } + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} nanoid@3.3.20: - resolution: - { - integrity: sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==, - } - engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } + resolution: {integrity: sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true natural-compare@1.4.0: - resolution: - { - integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, - } + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} neotraverse@0.6.18: - resolution: - { - integrity: sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==, - } - engines: { node: ">= 10" } + resolution: {integrity: sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==} + engines: {node: '>= 10'} nlcst-to-string@4.0.0: - resolution: - { - integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==, - } + resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==} node-exports-info@1.6.2: - resolution: - { - integrity: sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==} + engines: {node: '>= 0.4'} node-fetch-native@1.6.7: - resolution: - { - integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==, - } + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} node-mock-http@1.0.5: - resolution: - { - integrity: sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==, - } + resolution: {integrity: sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==} node-releases@2.0.60: - resolution: - { - integrity: sha512-OmO8+GM1JUZqXET720VrJCwkjUCXmCMQm0Z3UDDRbfNLHS4NEAvY60j5jiFv0tkKw2PNNlqOLhahVmwJIOLDIQ==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-OmO8+GM1JUZqXET720VrJCwkjUCXmCMQm0Z3UDDRbfNLHS4NEAvY60j5jiFv0tkKw2PNNlqOLhahVmwJIOLDIQ==} + engines: {node: '>=18'} normalize-path@3.0.0: - resolution: - { - integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} nth-check@2.1.1: - resolution: - { - integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==, - } + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} object-assign@4.1.1: - resolution: - { - integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} object-inspect@1.13.4: - resolution: - { - integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} object-keys@1.1.1: - resolution: - { - integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} object.assign@4.1.7: - resolution: - { - integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} object.entries@1.1.9: - resolution: - { - integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} object.fromentries@2.0.8: - resolution: - { - integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} object.values@1.2.1: - resolution: - { - integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} ofetch@1.5.1: - resolution: - { - integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==, - } + resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} ohash@2.0.12: - resolution: - { - integrity: sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==, - } + resolution: {integrity: sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==} oniguruma-parser@0.12.2: - resolution: - { - integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==, - } + resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} oniguruma-to-es@4.3.6: - resolution: - { - integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==, - } + resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} optionator@0.9.4: - resolution: - { - integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==, - } - engines: { node: ">= 0.8.0" } + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} own-keys@1.0.2: - resolution: - { - integrity: sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==} + engines: {node: '>= 0.4'} p-limit@3.1.0: - resolution: - { - integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} p-limit@6.2.0: - resolution: - { - integrity: sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==} + engines: {node: '>=18'} p-locate@5.0.0: - resolution: - { - integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} p-queue@8.1.1: - resolution: - { - integrity: sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==} + engines: {node: '>=18'} p-timeout@6.1.4: - resolution: - { - integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==, - } - engines: { node: ">=14.16" } + resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} + engines: {node: '>=14.16'} package-manager-detector@1.9.0: - resolution: - { - integrity: sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA==, - } + resolution: {integrity: sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA==} parent-module@1.0.1: - resolution: - { - integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} parse-latin@7.0.0: - resolution: - { - integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==, - } + resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==} parse5@7.3.0: - resolution: - { - integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==, - } + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} path-browserify@1.0.1: - resolution: - { - integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==, - } + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} path-exists@4.0.0: - resolution: - { - integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} path-key@3.1.1: - resolution: - { - integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} path-parse@1.0.7: - resolution: - { - integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, - } + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} piccolore@0.1.3: - resolution: - { - integrity: sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==, - } + resolution: {integrity: sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==} picocolors@1.1.1: - resolution: - { - integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, - } + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} picomatch@2.3.2: - resolution: - { - integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==, - } - engines: { node: ">=8.6" } + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} picomatch@4.0.7: - resolution: - { - integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + playwright-core@1.56.1: + resolution: {integrity: sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.56.1: + resolution: {integrity: sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==} + engines: {node: '>=18'} + hasBin: true possible-typed-array-names@1.1.0: - resolution: - { - integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} postcss@8.5.31: - resolution: - { - integrity: sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==, - } - engines: { node: ^10 || ^12 || >=14 } + resolution: {integrity: sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==} + engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: - resolution: - { - integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==, - } - engines: { node: ">= 0.8.0" } + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} prettier-plugin-tailwindcss@0.6.14: - resolution: - { - integrity: sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==, - } - engines: { node: ">=14.21.3" } + resolution: {integrity: sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==} + engines: {node: '>=14.21.3'} peerDependencies: - "@ianvs/prettier-plugin-sort-imports": "*" - "@prettier/plugin-hermes": "*" - "@prettier/plugin-oxc": "*" - "@prettier/plugin-pug": "*" - "@shopify/prettier-plugin-liquid": "*" - "@trivago/prettier-plugin-sort-imports": "*" - "@zackad/prettier-plugin-twig": "*" + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-hermes': '*' + '@prettier/plugin-oxc': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + '@zackad/prettier-plugin-twig': '*' prettier: ^3.0 - prettier-plugin-astro: "*" - prettier-plugin-css-order: "*" - prettier-plugin-import-sort: "*" - prettier-plugin-jsdoc: "*" - prettier-plugin-marko: "*" - prettier-plugin-multiline-arrays: "*" - prettier-plugin-organize-attributes: "*" - prettier-plugin-organize-imports: "*" - prettier-plugin-sort-imports: "*" - prettier-plugin-style-order: "*" - prettier-plugin-svelte: "*" + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-import-sort: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-multiline-arrays: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-sort-imports: '*' + prettier-plugin-style-order: '*' + prettier-plugin-svelte: '*' peerDependenciesMeta: - "@ianvs/prettier-plugin-sort-imports": + '@ianvs/prettier-plugin-sort-imports': optional: true - "@prettier/plugin-hermes": + '@prettier/plugin-hermes': optional: true - "@prettier/plugin-oxc": + '@prettier/plugin-oxc': optional: true - "@prettier/plugin-pug": + '@prettier/plugin-pug': optional: true - "@shopify/prettier-plugin-liquid": + '@shopify/prettier-plugin-liquid': optional: true - "@trivago/prettier-plugin-sort-imports": + '@trivago/prettier-plugin-sort-imports': optional: true - "@zackad/prettier-plugin-twig": + '@zackad/prettier-plugin-twig': optional: true prettier-plugin-astro: optional: true @@ -4369,568 +2729,346 @@ packages: optional: true prettier@3.9.10: - resolution: - { - integrity: sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA==} + engines: {node: '>=14'} hasBin: true prismjs@1.30.0: - resolution: - { - integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} prompts@2.4.2: - resolution: - { - integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==, - } - engines: { node: ">= 6" } + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} prop-types@15.8.1: - resolution: - { - integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==, - } + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} property-information@7.2.0: - resolution: - { - integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==, - } + resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} punycode@2.3.1: - resolution: - { - integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} radix3@1.1.2: - resolution: - { - integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==, - } + resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} react-dom@18.3.1: - resolution: - { - integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==, - } + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} peerDependencies: react: ^18.3.1 react-is@16.13.1: - resolution: - { - integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==, - } + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} react-refresh@0.17.0: - resolution: - { - integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} react@18.3.1: - resolution: - { - integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} readdirp@5.1.1: - resolution: - { - integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==, - } - engines: { node: ">= 20.19.0" } + resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} + engines: {node: '>= 20.19.0'} reflect.getprototypeof@1.0.10: - resolution: - { - integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} regex-recursion@6.0.2: - resolution: - { - integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==, - } + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} regex-utilities@2.3.0: - resolution: - { - integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==, - } + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} regex@6.1.0: - resolution: - { - integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==, - } + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} regexp.prototype.flags@1.5.4: - resolution: - { - integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} rehype-parse@9.0.1: - resolution: - { - integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==, - } + resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==} rehype-raw@7.0.0: - resolution: - { - integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==, - } + resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} rehype-stringify@10.0.1: - resolution: - { - integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==, - } + resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==} rehype@13.0.2: - resolution: - { - integrity: sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==, - } + resolution: {integrity: sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==} remark-gfm@4.0.1: - resolution: - { - integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==, - } + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} remark-parse@11.0.0: - resolution: - { - integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==, - } + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} remark-rehype@11.1.2: - resolution: - { - integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==, - } + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} remark-smartypants@3.0.3: - resolution: - { - integrity: sha512-gCaK+ndZ0hYezlqFegHFCVh2CQemsi0Npdh1qVM9bxlUFknjkbP6VmojWhddOCrbK0PbbacmYLWfTULRiT1eWA==, - } - engines: { node: ">=16.0.0" } + resolution: {integrity: sha512-gCaK+ndZ0hYezlqFegHFCVh2CQemsi0Npdh1qVM9bxlUFknjkbP6VmojWhddOCrbK0PbbacmYLWfTULRiT1eWA==} + engines: {node: '>=16.0.0'} remark-stringify@11.0.0: - resolution: - { - integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==, - } + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} request-light@0.5.8: - resolution: - { - integrity: sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==, - } + resolution: {integrity: sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==} request-light@0.7.0: - resolution: - { - integrity: sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==, - } + resolution: {integrity: sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==} require-from-string@2.0.2: - resolution: - { - integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} resolve-from@4.0.0: - resolution: - { - integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} resolve@2.0.0-next.7: - resolution: - { - integrity: sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==} + engines: {node: '>= 0.4'} hasBin: true retext-latin@4.0.0: - resolution: - { - integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==, - } + resolution: {integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==} retext-smartypants@6.2.0: - resolution: - { - integrity: sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==, - } + resolution: {integrity: sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==} retext-stringify@4.0.0: - resolution: - { - integrity: sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==, - } + resolution: {integrity: sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==} retext@9.0.0: - resolution: - { - integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==, - } + resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==} rollup@4.65.0: - resolution: - { - integrity: sha512-QVCvQUTGDN6JRwi3jVGCY3H4aTHvi9rX0LyoBMkGUvdh/Z6O51CzcLz85BWZaTCDGhl6ySGqYZs+xI1jmPqGcw==, - } - engines: { node: ">=18.0.0", npm: ">=8.0.0" } + resolution: {integrity: sha512-QVCvQUTGDN6JRwi3jVGCY3H4aTHvi9rX0LyoBMkGUvdh/Z6O51CzcLz85BWZaTCDGhl6ySGqYZs+xI1jmPqGcw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true safe-array-concat@1.1.4: - resolution: - { - integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==, - } - engines: { node: ">=0.4" } + resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} + engines: {node: '>=0.4'} safe-push-apply@1.0.0: - resolution: - { - integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} safe-regex-test@1.1.0: - resolution: - { - integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} sax@1.6.1: - resolution: - { - integrity: sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==, - } - engines: { node: ">=11.0.0" } + resolution: {integrity: sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==} + engines: {node: '>=11.0.0'} scheduler@0.23.2: - resolution: - { - integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==, - } + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} semver@6.3.1: - resolution: - { - integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==, - } + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true semver@7.8.5: - resolution: - { - integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} hasBin: true set-function-length@1.2.2: - resolution: - { - integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} set-function-name@2.0.2: - resolution: - { - integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} set-proto@1.0.0: - resolution: - { - integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} sharp@0.34.5: - resolution: - { - integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} shebang-command@2.0.0: - resolution: - { - integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} shebang-regex@3.0.0: - resolution: - { - integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} shiki@3.23.0: - resolution: - { - integrity: sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==, - } + resolution: {integrity: sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==} side-channel-list@1.0.1: - resolution: - { - integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} side-channel-map@1.0.1: - resolution: - { - integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} side-channel-weakmap@1.0.2: - resolution: - { - integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} side-channel@1.1.1: - resolution: - { - integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} sisteransi@1.0.5: - resolution: - { - integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==, - } + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} smol-toml@1.9.1: - resolution: - { - integrity: sha512-j7WyPdf0CJoutMzMoPLF/bVXHtKZIT9Rb4eGeEbWw8X2t5oirjPgpL+OpaENZrILYaXqYwnVH+qgUwVbl+pUJQ==, - } - engines: { node: ">= 18" } + resolution: {integrity: sha512-j7WyPdf0CJoutMzMoPLF/bVXHtKZIT9Rb4eGeEbWw8X2t5oirjPgpL+OpaENZrILYaXqYwnVH+qgUwVbl+pUJQ==} + engines: {node: '>= 18'} source-map-js@1.2.2: - resolution: - { - integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} + engines: {node: '>=0.10.0'} space-separated-tokens@2.0.2: - resolution: - { - integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==, - } + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} stop-iteration-iterator@1.1.0: - resolution: - { - integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} string-width@4.2.3: - resolution: - { - integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} string-width@7.2.0: - resolution: - { - integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} string-width@8.3.0: - resolution: - { - integrity: sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==, - } - engines: { node: ">=20" } + resolution: {integrity: sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==} + engines: {node: '>=20'} string.prototype.matchall@4.1.0: - resolution: - { - integrity: sha512-tHNHTxInrYLCga9O9YGxWA3G9/nnzQw8UGAyqGx3Ar1pSTTzIuM4woFSq4SowkXCjJIwq5sIiQvEfRI9tCH1qQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-tHNHTxInrYLCga9O9YGxWA3G9/nnzQw8UGAyqGx3Ar1pSTTzIuM4woFSq4SowkXCjJIwq5sIiQvEfRI9tCH1qQ==} + engines: {node: '>= 0.4'} string.prototype.repeat@1.0.0: - resolution: - { - integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==, - } + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} string.prototype.trim@1.2.11: - resolution: - { - integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==} + engines: {node: '>= 0.4'} string.prototype.trimend@1.0.10: - resolution: - { - integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==} + engines: {node: '>= 0.4'} string.prototype.trimstart@1.0.8: - resolution: - { - integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} stringify-entities@4.0.4: - resolution: - { - integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==, - } + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} strip-ansi@6.0.1: - resolution: - { - integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} strip-ansi@7.2.0: - resolution: - { - integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} strip-json-comments@3.1.1: - resolution: - { - integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strip-literal@3.1.0: + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} supports-color@7.2.0: - resolution: - { - integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} supports-preserve-symlinks-flag@1.0.0: - resolution: - { - integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} svgo@4.1.0: - resolution: - { - integrity: sha512-bkxnTg1kSU0guhIBmibA6UUhrQmPVA1XsQLN+ylCd+UWzbnLkySOcXpyk1mrl05f+pcaCx2eHb+sp6BgMZWX+Q==, - } - engines: { node: ">=16" } + resolution: {integrity: sha512-bkxnTg1kSU0guhIBmibA6UUhrQmPVA1XsQLN+ylCd+UWzbnLkySOcXpyk1mrl05f+pcaCx2eHb+sp6BgMZWX+Q==} + engines: {node: '>=16'} hasBin: true tailwind-merge@2.6.1: - resolution: - { - integrity: sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ==, - } + resolution: {integrity: sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ==} tailwindcss@4.3.3: - resolution: - { - integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==, - } + resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} tapable@2.3.3: - resolution: - { - integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} tiny-inflate@1.0.3: - resolution: - { - integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==, - } + resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} tinyexec@1.3.1: - resolution: - { - integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} + engines: {node: '>=18'} tinyglobby@0.2.17: - resolution: - { - integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==, - } - engines: { node: ">=12.0.0" } + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.6: + resolution: {integrity: sha512-u8KszXvGfU68hVcZpRHKG28T0krMuv2G5nDhiHaMLen/gIuFEgIJhaJuO69qjnXg5paSrbPMFfx3brNuN8eVSg==} + engines: {node: '>=14.0.0'} trim-lines@3.0.1: - resolution: - { - integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==, - } + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} trough@2.2.0: - resolution: - { - integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==, - } + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} ts-api-utils@2.5.0: - resolution: - { - integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==, - } - engines: { node: ">=18.12" } + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} peerDependencies: - typescript: ">=4.8.4" + typescript: '>=4.8.4' tsconfck@3.1.6: - resolution: - { - integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==, - } - engines: { node: ^18 || >=20 } + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} deprecated: unmaintained hasBin: true peerDependencies: @@ -4940,224 +3078,143 @@ packages: optional: true tslib@2.8.1: - resolution: - { - integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==, - } + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} type-check@0.4.0: - resolution: - { - integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==, - } - engines: { node: ">= 0.8.0" } + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} type-fest@4.41.0: - resolution: - { - integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==, - } - engines: { node: ">=16" } + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} typed-array-buffer@1.0.3: - resolution: - { - integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} typed-array-byte-length@1.0.3: - resolution: - { - integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} typed-array-byte-offset@1.0.5: - resolution: - { - integrity: sha512-0FHJvLPqZ7KJzp17O13jfsAjsqazgrxBu2zEK95PmUz8lv2+GjRuxUInCr2Rk9Dms3ihN21zJ929ZO43yJ95QQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-0FHJvLPqZ7KJzp17O13jfsAjsqazgrxBu2zEK95PmUz8lv2+GjRuxUInCr2Rk9Dms3ihN21zJ929ZO43yJ95QQ==} + engines: {node: '>= 0.4'} typed-array-length@1.0.8: - resolution: - { - integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} + engines: {node: '>= 0.4'} typesafe-path@0.2.2: - resolution: - { - integrity: sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==, - } + resolution: {integrity: sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==} typescript-auto-import-cache@0.3.6: - resolution: - { - integrity: sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ==, - } + resolution: {integrity: sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ==} typescript@5.9.3: - resolution: - { - integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==, - } - engines: { node: ">=14.17" } + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} hasBin: true ufo@1.6.4: - resolution: - { - integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==, - } + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} ultrahtml@1.7.0: - resolution: - { - integrity: sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==, - } + resolution: {integrity: sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==} unbox-primitive@1.1.0: - resolution: - { - integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} uncrypto@0.1.3: - resolution: - { - integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==, - } + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} undici@8.12.3: - resolution: - { - integrity: sha512-+fdndUTwBP86cVjihTp21hZHo7d6oj1c/47XPA3lL8mM0XHs+C4EkcjmgRsFnFk2f/UONGDy9ItBItmXsLOQHw==, - } - engines: { node: ">=22.19.0" } + resolution: {integrity: sha512-+fdndUTwBP86cVjihTp21hZHo7d6oj1c/47XPA3lL8mM0XHs+C4EkcjmgRsFnFk2f/UONGDy9ItBItmXsLOQHw==} + engines: {node: '>=22.19.0'} unified@11.0.5: - resolution: - { - integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==, - } + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} unifont@0.7.5: - resolution: - { - integrity: sha512-ULe/Cs+ZIsq+dcFofNkhqielCrUJnb5mr+Yc4EBM2VlL+6OZR6+cjtI2mT1bJvRBrVncqHAbLURxmPLcCXzWMg==, - } + resolution: {integrity: sha512-ULe/Cs+ZIsq+dcFofNkhqielCrUJnb5mr+Yc4EBM2VlL+6OZR6+cjtI2mT1bJvRBrVncqHAbLURxmPLcCXzWMg==} unist-util-find-after@5.0.0: - resolution: - { - integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==, - } + resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} unist-util-is@6.0.1: - resolution: - { - integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==, - } + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} unist-util-modify-children@4.0.0: - resolution: - { - integrity: sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==, - } + resolution: {integrity: sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==} unist-util-position@5.0.0: - resolution: - { - integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==, - } + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} unist-util-remove-position@5.0.0: - resolution: - { - integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==, - } + resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} unist-util-stringify-position@4.0.0: - resolution: - { - integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==, - } + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} unist-util-visit-children@3.0.0: - resolution: - { - integrity: sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==, - } + resolution: {integrity: sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==} unist-util-visit-parents@6.0.2: - resolution: - { - integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==, - } + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} unist-util-visit@5.1.0: - resolution: - { - integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==, - } + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} unstorage@1.17.6: - resolution: - { - integrity: sha512-gjKtcmn/KwZuJZO9o2sc0esAQnhmTw0LviDHwevTXbJMYs0dRTp8ZEEWXHU0vTpzbN3cA0yxlTeyXssMp3zDqw==, - } + resolution: {integrity: sha512-gjKtcmn/KwZuJZO9o2sc0esAQnhmTw0LviDHwevTXbJMYs0dRTp8ZEEWXHU0vTpzbN3cA0yxlTeyXssMp3zDqw==} peerDependencies: - "@azure/app-configuration": ^1.8.0 - "@azure/cosmos": ^4.2.0 - "@azure/data-tables": ^13.3.0 - "@azure/identity": ^4.6.0 - "@azure/keyvault-secrets": ^4.9.0 - "@azure/storage-blob": ^12.26.0 - "@capacitor/preferences": ^6 || ^7 || ^8 - "@deno/kv": ">=0.9.0" - "@netlify/blobs": ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 - "@planetscale/database": ^1.19.0 - "@upstash/redis": ^1.34.3 - "@vercel/blob": ">=0.27.1" - "@vercel/functions": ^2.2.12 || ^3.0.0 - "@vercel/kv": ^1 || ^2 || ^3 + '@azure/app-configuration': ^1.8.0 + '@azure/cosmos': ^4.2.0 + '@azure/data-tables': ^13.3.0 + '@azure/identity': ^4.6.0 + '@azure/keyvault-secrets': ^4.9.0 + '@azure/storage-blob': ^12.26.0 + '@capacitor/preferences': ^6 || ^7 || ^8 + '@deno/kv': '>=0.9.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.34.3 + '@vercel/blob': '>=0.27.1' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1 || ^2 || ^3 aws4fetch: ^1.0.20 - db0: ">=0.2.1" + db0: '>=0.2.1' idb-keyval: ^6.2.1 ioredis: ^5.4.2 uploadthing: ^7.4.4 peerDependenciesMeta: - "@azure/app-configuration": + '@azure/app-configuration': optional: true - "@azure/cosmos": + '@azure/cosmos': optional: true - "@azure/data-tables": + '@azure/data-tables': optional: true - "@azure/identity": + '@azure/identity': optional: true - "@azure/keyvault-secrets": + '@azure/keyvault-secrets': optional: true - "@azure/storage-blob": + '@azure/storage-blob': optional: true - "@capacitor/preferences": + '@capacitor/preferences': optional: true - "@deno/kv": + '@deno/kv': optional: true - "@netlify/blobs": + '@netlify/blobs': optional: true - "@planetscale/database": + '@planetscale/database': optional: true - "@upstash/redis": + '@upstash/redis': optional: true - "@vercel/blob": + '@vercel/blob': optional: true - "@vercel/functions": + '@vercel/functions': optional: true - "@vercel/kv": + '@vercel/kv': optional: true aws4fetch: optional: true @@ -5171,59 +3228,46 @@ packages: optional: true update-browserslist-db@1.3.4: - resolution: - { - integrity: sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==, - } + resolution: {integrity: sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==} hasBin: true peerDependencies: - browserslist: ">= 4.21.0" + browserslist: '>= 4.21.0' uri-js@4.4.1: - resolution: - { - integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, - } + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} vfile-location@5.0.3: - resolution: - { - integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==, - } + resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} vfile-message@4.0.3: - resolution: - { - integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==, - } + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} vfile@6.0.3: - resolution: - { - integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==, - } + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true vite@6.4.5: - resolution: - { - integrity: sha512-4enwjFfi4IwtEh0w+jPzpLVOFQiPHmmzPxcHXQOyEur47o6u0jE573DmGpjA+7irXoMLYRZD5ZaFUEneS3abPg==, - } - engines: { node: ^18.0.0 || ^20.0.0 || >=22.0.0 } + resolution: {integrity: sha512-4enwjFfi4IwtEh0w+jPzpLVOFQiPHmmzPxcHXQOyEur47o6u0jE573DmGpjA+7irXoMLYRZD5ZaFUEneS3abPg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: - "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 - jiti: ">=1.21.0" - less: "*" + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' lightningcss: ^1.21.0 - sass: "*" - sass-embedded: "*" - stylus: "*" - sugarss: "*" + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' terser: ^5.16.0 tsx: ^4.8.1 yaml: ^2.4.2 peerDependenciesMeta: - "@types/node": + '@types/node': optional: true jiti: optional: true @@ -5247,406 +3291,296 @@ packages: optional: true vitefu@1.1.3: - resolution: - { - integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==, - } + resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==} peerDependencies: vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: vite: optional: true + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + volar-service-css@0.0.71: - resolution: - { - integrity: sha512-wRRFt9BpjMKCazcgOh67MSjUjiWUCAh99DyYSDIOTuxaRjEtDC7PpB0k1Y1wbJIW/pVtMUSVbpPo3UGSm0Byxw==, - } + resolution: {integrity: sha512-wRRFt9BpjMKCazcgOh67MSjUjiWUCAh99DyYSDIOTuxaRjEtDC7PpB0k1Y1wbJIW/pVtMUSVbpPo3UGSm0Byxw==} peerDependencies: - "@volar/language-service": ~2.4.0 + '@volar/language-service': ~2.4.0 peerDependenciesMeta: - "@volar/language-service": + '@volar/language-service': optional: true volar-service-emmet@0.0.71: - resolution: - { - integrity: sha512-zqjzt6bN95e3CUstBm0PBFAJnrfz0ZAARka87fart46/gNCLLuP3Vujy8V/J8HEziTFLnfkgIASLFYPUhonJcA==, - } + resolution: {integrity: sha512-zqjzt6bN95e3CUstBm0PBFAJnrfz0ZAARka87fart46/gNCLLuP3Vujy8V/J8HEziTFLnfkgIASLFYPUhonJcA==} peerDependencies: - "@volar/language-service": ~2.4.0 + '@volar/language-service': ~2.4.0 peerDependenciesMeta: - "@volar/language-service": + '@volar/language-service': optional: true volar-service-html@0.0.71: - resolution: - { - integrity: sha512-e8tHPhgQ7ooLfudAEIku+kgd9pWkq3SSz8RbnQDI1+Eb8wbenkLGHqoirLqz5ORLV6wIMr2Iv08RWBG5eOcgpw==, - } + resolution: {integrity: sha512-e8tHPhgQ7ooLfudAEIku+kgd9pWkq3SSz8RbnQDI1+Eb8wbenkLGHqoirLqz5ORLV6wIMr2Iv08RWBG5eOcgpw==} peerDependencies: - "@volar/language-service": ~2.4.0 + '@volar/language-service': ~2.4.0 peerDependenciesMeta: - "@volar/language-service": + '@volar/language-service': optional: true volar-service-prettier@0.0.71: - resolution: - { - integrity: sha512-Rz7JVH3qD108UCdmIEiZvOBNljMt2nLFdbN8AXcDfn7xD9F5I2aCIsDVqBbXw21PsnxG0b7MfwtNF+zPS/NKUg==, - } + resolution: {integrity: sha512-Rz7JVH3qD108UCdmIEiZvOBNljMt2nLFdbN8AXcDfn7xD9F5I2aCIsDVqBbXw21PsnxG0b7MfwtNF+zPS/NKUg==} peerDependencies: - "@volar/language-service": ~2.4.0 + '@volar/language-service': ~2.4.0 prettier: ^2.2 || ^3.0 peerDependenciesMeta: - "@volar/language-service": + '@volar/language-service': optional: true prettier: optional: true volar-service-typescript-twoslash-queries@0.0.71: - resolution: - { - integrity: sha512-9K2k72s4n7rV9s4bX0MyjbX9iBribvKZbBJKuEmTCZfeWJXs6Yh7bGpY4eoc7UufAjvpheBqwyZCOIPBvxCv0A==, - } + resolution: {integrity: sha512-9K2k72s4n7rV9s4bX0MyjbX9iBribvKZbBJKuEmTCZfeWJXs6Yh7bGpY4eoc7UufAjvpheBqwyZCOIPBvxCv0A==} peerDependencies: - "@volar/language-service": ~2.4.0 - typescript: "*" + '@volar/language-service': ~2.4.0 + typescript: '*' peerDependenciesMeta: - "@volar/language-service": + '@volar/language-service': optional: true typescript: optional: true volar-service-typescript@0.0.71: - resolution: - { - integrity: sha512-yTtM/BVT6hoyEYnDtaCyAtNhdNeS/mhTTABlBOdw3NNiRBUin3IznFJpgfjer4c6RYopiPjjQjc9VFhxVl1mLw==, - } + resolution: {integrity: sha512-yTtM/BVT6hoyEYnDtaCyAtNhdNeS/mhTTABlBOdw3NNiRBUin3IznFJpgfjer4c6RYopiPjjQjc9VFhxVl1mLw==} peerDependencies: - "@volar/language-service": ~2.4.0 - typescript: "*" + '@volar/language-service': ~2.4.0 + typescript: '*' peerDependenciesMeta: - "@volar/language-service": + '@volar/language-service': optional: true typescript: optional: true volar-service-yaml@0.0.71: - resolution: - { - integrity: sha512-qYGWGuVpUTnZGu5P/CR4KLK4aIR8RrcVnmfZ2eRcj9q/I8VZCoC5yy9FtEvfNvnDp4MU17yhdJcvpQPIqhJS2Q==, - } + resolution: {integrity: sha512-qYGWGuVpUTnZGu5P/CR4KLK4aIR8RrcVnmfZ2eRcj9q/I8VZCoC5yy9FtEvfNvnDp4MU17yhdJcvpQPIqhJS2Q==} peerDependencies: - "@volar/language-service": ~2.4.0 + '@volar/language-service': ~2.4.0 peerDependenciesMeta: - "@volar/language-service": + '@volar/language-service': optional: true vscode-css-languageservice@6.3.10: - resolution: - { - integrity: sha512-eq5N9Er3fC4vA9zd9EFhyBG90wtCCuXgRSpAndaOgXMh1Wgep5lBgRIeDgjZBW9pa+332yC9+49cZMW8jcL3MA==, - } + resolution: {integrity: sha512-eq5N9Er3fC4vA9zd9EFhyBG90wtCCuXgRSpAndaOgXMh1Wgep5lBgRIeDgjZBW9pa+332yC9+49cZMW8jcL3MA==} vscode-html-languageservice@5.6.2: - resolution: - { - integrity: sha512-ulCrSnFnfQ16YzvwnYUgEbUEl/ZG7u2eV27YhvLObSHKkb8fw1Z9cgsnUwjTEeDIdJDoTDTDpxuhQwoenoLNMg==, - } + resolution: {integrity: sha512-ulCrSnFnfQ16YzvwnYUgEbUEl/ZG7u2eV27YhvLObSHKkb8fw1Z9cgsnUwjTEeDIdJDoTDTDpxuhQwoenoLNMg==} vscode-json-languageservice@4.1.8: - resolution: - { - integrity: sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg==, - } - engines: { npm: ">=7.0.0" } + resolution: {integrity: sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg==} + engines: {npm: '>=7.0.0'} vscode-jsonrpc@10.0.0: - resolution: - { - integrity: sha512-USSfxg0vDIpTQqlQBGxF3mBARyoS8cU7LHLpbYNh6dOJt32dlskxPWB0Irrh5TllTmyf7oNLAvqDqzklbwwnpA==, - } - engines: { node: ">=14.17.0" } + resolution: {integrity: sha512-USSfxg0vDIpTQqlQBGxF3mBARyoS8cU7LHLpbYNh6dOJt32dlskxPWB0Irrh5TllTmyf7oNLAvqDqzklbwwnpA==} + engines: {node: '>=14.17.0'} vscode-jsonrpc@8.2.0: - resolution: - { - integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==, - } - engines: { node: ">=14.0.0" } + resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} + engines: {node: '>=14.0.0'} vscode-languageserver-protocol@3.17.5: - resolution: - { - integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==, - } + resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} vscode-languageserver-protocol@3.18.5: - resolution: - { - integrity: sha512-FW4fBMzZ99N8xeob4py0l050wgCoRgTpEq5Xcsb9lM1bc29UKGR/wSV+4knaqFxssFHajy7OzNiKP/m3U1ZEuA==, - } + resolution: {integrity: sha512-FW4fBMzZ99N8xeob4py0l050wgCoRgTpEq5Xcsb9lM1bc29UKGR/wSV+4knaqFxssFHajy7OzNiKP/m3U1ZEuA==} vscode-languageserver-textdocument@1.0.16: - resolution: - { - integrity: sha512-qeNEuOi9a3qkqRf7Gfez2WL2VwlBTLCNekHBGO9kxPVUp9XuY15yfTJ9jwvOwKI0I2MhUUfZq95NSp+LK1CLQg==, - } + resolution: {integrity: sha512-qeNEuOi9a3qkqRf7Gfez2WL2VwlBTLCNekHBGO9kxPVUp9XuY15yfTJ9jwvOwKI0I2MhUUfZq95NSp+LK1CLQg==} vscode-languageserver-types@3.17.5: - resolution: - { - integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==, - } + resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} vscode-languageserver-types@3.18.5: - resolution: - { - integrity: sha512-kPd6+JQNz3HMJvjKlqUxERSlZHAntl/aGpsk7ImwIh0ewDKDcjgAEyVSYciPiOJD3ftbK9IrfiqUeTZLSbCizw==, - } + resolution: {integrity: sha512-kPd6+JQNz3HMJvjKlqUxERSlZHAntl/aGpsk7ImwIh0ewDKDcjgAEyVSYciPiOJD3ftbK9IrfiqUeTZLSbCizw==} vscode-languageserver@9.0.1: - resolution: - { - integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==, - } + resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} hasBin: true vscode-nls@5.2.0: - resolution: - { - integrity: sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==, - } + resolution: {integrity: sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==} vscode-uri@3.2.0: - resolution: - { - integrity: sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==, - } + resolution: {integrity: sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==} web-namespaces@2.0.1: - resolution: - { - integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==, - } + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} which-boxed-primitive@1.1.1: - resolution: - { - integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} which-builtin-type@1.2.1: - resolution: - { - integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} which-collection@1.0.2: - resolution: - { - integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} which-pm-runs@1.1.0: - resolution: - { - integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} + engines: {node: '>=4'} which-typed-array@1.1.24: - resolution: - { - integrity: sha512-wk4Mf4pR5mRP7eYuuTBCIQ9d0ud2Fv2jRLQpfgnRjbOxAFHmjKFValgTpitVKzJJS8ajnYQV2Du1SZ8j6b/EUQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-wk4Mf4pR5mRP7eYuuTBCIQ9d0ud2Fv2jRLQpfgnRjbOxAFHmjKFValgTpitVKzJJS8ajnYQV2Du1SZ8j6b/EUQ==} + engines: {node: '>= 0.4'} which@2.0.2: - resolution: - { - integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, - } - engines: { node: ">= 8" } + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} hasBin: true widest-line@5.0.0: - resolution: - { - integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} + engines: {node: '>=18'} word-wrap@1.2.5: - resolution: - { - integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} wrap-ansi@9.0.2: - resolution: - { - integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} xxhash-wasm@1.1.0: - resolution: - { - integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==, - } + resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} y18n@5.0.8: - resolution: - { - integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} yallist@3.1.1: - resolution: - { - integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==, - } + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} yaml-language-server@1.23.0: - resolution: - { - integrity: sha512-3qVyCOexLCWw06PQa5kRPwvMWMZ/eZeCRWUvgD6a0OkqL/4iCnxy2WumbWifa937Uo5xhyWJ0uxlU39ljhNh7A==, - } + resolution: {integrity: sha512-3qVyCOexLCWw06PQa5kRPwvMWMZ/eZeCRWUvgD6a0OkqL/4iCnxy2WumbWifa937Uo5xhyWJ0uxlU39ljhNh7A==} hasBin: true yaml@2.8.3: - resolution: - { - integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==, - } - engines: { node: ">= 14.6" } + resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} + engines: {node: '>= 14.6'} hasBin: true yaml@2.9.1: - resolution: - { - integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==, - } - engines: { node: ">= 14.6" } + resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==} + engines: {node: '>= 14.6'} hasBin: true yargs-parser@21.1.1: - resolution: - { - integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} yargs-parser@22.0.0: - resolution: - { - integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==, - } - engines: { node: ^20.19.0 || ^22.12.0 || >=23 } + resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} yargs@18.2.0: - resolution: - { - integrity: sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==, - } - engines: { node: ^20.19.0 || ^22.12.0 || >=23 } + resolution: {integrity: sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} yocto-queue@0.1.0: - resolution: - { - integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} yocto-queue@1.2.2: - resolution: - { - integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==, - } - engines: { node: ">=12.20" } + resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} + engines: {node: '>=12.20'} yocto-spinner@0.2.3: - resolution: - { - integrity: sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==, - } - engines: { node: ">=18.19" } + resolution: {integrity: sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==} + engines: {node: '>=18.19'} yoctocolors@2.2.0: - resolution: - { - integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==} + engines: {node: '>=18'} zod-to-json-schema@3.25.2: - resolution: - { - integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==, - } + resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} peerDependencies: zod: ^3.25.28 || ^4 zod-to-ts@1.2.0: - resolution: - { - integrity: sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==, - } + resolution: {integrity: sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==} peerDependencies: typescript: ^4.9.4 || ^5.0.2 zod: ^3 zod@3.25.76: - resolution: - { - integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==, - } + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} zwitch@2.0.4: - resolution: - { - integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==, - } + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} snapshots: - "@astrojs/astro2tsx@0.1.3(@emnapi/runtime@1.11.3)": + + '@astrojs/astro2tsx@0.1.3(@emnapi/runtime@1.11.3)': dependencies: - "@emnapi/core": 1.11.3 - "@napi-rs/wasm-runtime": 1.2.4(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) + '@emnapi/core': 1.11.3 + '@napi-rs/wasm-runtime': 1.2.4(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) transitivePeerDependencies: - - "@emnapi/runtime" + - '@emnapi/runtime' - "@astrojs/check@0.9.12(@emnapi/runtime@1.11.3)(prettier@3.9.10)(typescript@5.9.3)": + '@astrojs/check@0.9.12(@emnapi/runtime@1.11.3)(prettier@3.9.10)(typescript@5.9.3)': dependencies: - "@astrojs/language-server": 2.17.3(@emnapi/runtime@1.11.3)(prettier@3.9.10)(typescript@5.9.3) + '@astrojs/language-server': 2.17.3(@emnapi/runtime@1.11.3)(prettier@3.9.10)(typescript@5.9.3) chokidar: 5.0.0 kleur: 4.1.5 typescript: 5.9.3 yargs: 18.2.0 transitivePeerDependencies: - - "@emnapi/runtime" + - '@emnapi/runtime' - prettier - prettier-plugin-astro - "@astrojs/compiler@2.13.1": {} + '@astrojs/compiler@2.13.1': {} - "@astrojs/internal-helpers@0.7.6": {} + '@astrojs/internal-helpers@0.7.6': {} - "@astrojs/language-server@2.17.3(@emnapi/runtime@1.11.3)(prettier@3.9.10)(typescript@5.9.3)": + '@astrojs/language-server@2.17.3(@emnapi/runtime@1.11.3)(prettier@3.9.10)(typescript@5.9.3)': dependencies: - "@astrojs/astro2tsx": 0.1.3(@emnapi/runtime@1.11.3) - "@astrojs/yaml2ts": 0.2.4 - "@volar/kit": 2.4.30(typescript@5.9.3) - "@volar/language-core": 2.4.30 - "@volar/language-server": 2.4.30(typescript@5.9.3) - "@volar/language-service": 2.4.30(typescript@5.9.3) + '@astrojs/astro2tsx': 0.1.3(@emnapi/runtime@1.11.3) + '@astrojs/yaml2ts': 0.2.4 + '@volar/kit': 2.4.30(typescript@5.9.3) + '@volar/language-core': 2.4.30 + '@volar/language-server': 2.4.30(typescript@5.9.3) + '@volar/language-service': 2.4.30(typescript@5.9.3) muggle-string: 0.4.1 tinyglobby: 0.2.17 volar-service-css: 0.0.71(@volar/language-service@2.4.30(typescript@5.9.3)) @@ -5661,13 +3595,13 @@ snapshots: optionalDependencies: prettier: 3.9.10 transitivePeerDependencies: - - "@emnapi/runtime" + - '@emnapi/runtime' - typescript - "@astrojs/markdown-remark@6.3.11(supports-color@7.2.0)": + '@astrojs/markdown-remark@6.3.11(supports-color@7.2.0)': dependencies: - "@astrojs/internal-helpers": 0.7.6 - "@astrojs/prism": 3.3.0 + '@astrojs/internal-helpers': 0.7.6 + '@astrojs/prism': 3.3.0 github-slugger: 2.0.0 hast-util-from-html: 2.0.3 hast-util-to-text: 4.0.2 @@ -5690,21 +3624,21 @@ snapshots: transitivePeerDependencies: - supports-color - "@astrojs/prism@3.3.0": + '@astrojs/prism@3.3.0': dependencies: prismjs: 1.30.0 - "@astrojs/react@4.4.2(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(jiti@2.7.0)(lightningcss@1.32.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@7.2.0)(yaml@2.9.1)": + '@astrojs/react@4.4.2(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(jiti@2.7.0)(lightningcss@1.32.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@7.2.0)(yaml@2.9.1)': dependencies: - "@types/react": 18.3.31 - "@types/react-dom": 18.3.7(@types/react@18.3.31) - "@vitejs/plugin-react": 4.7.0(supports-color@7.2.0)(vite@6.4.5(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.1)) + '@types/react': 18.3.31 + '@types/react-dom': 18.3.7(@types/react@18.3.31) + '@vitejs/plugin-react': 4.7.0(supports-color@7.2.0)(vite@6.4.5(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.1)) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) ultrahtml: 1.7.0 vite: 6.4.5(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.1) transitivePeerDependencies: - - "@types/node" + - '@types/node' - jiti - less - lightningcss @@ -5717,7 +3651,7 @@ snapshots: - tsx - yaml - "@astrojs/telemetry@3.3.0(supports-color@7.2.0)": + '@astrojs/telemetry@3.3.0(supports-color@7.2.0)': dependencies: ci-info: 4.4.0 debug: 4.4.3(supports-color@7.2.0) @@ -5729,30 +3663,30 @@ snapshots: transitivePeerDependencies: - supports-color - "@astrojs/yaml2ts@0.2.4": + '@astrojs/yaml2ts@0.2.4': dependencies: yaml: 2.9.1 - "@babel/code-frame@7.29.7": + '@babel/code-frame@7.29.7': dependencies: - "@babel/helper-validator-identifier": 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 - "@babel/compat-data@7.29.7": {} + '@babel/compat-data@7.29.7': {} - "@babel/core@7.29.7(supports-color@7.2.0)": + '@babel/core@7.29.7(supports-color@7.2.0)': dependencies: - "@babel/code-frame": 7.29.7 - "@babel/generator": 7.29.8 - "@babel/helper-compilation-targets": 7.29.7 - "@babel/helper-module-transforms": 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) - "@babel/helpers": 7.29.10 - "@babel/parser": 7.29.9 - "@babel/template": 7.29.7 - "@babel/traverse": 7.29.10(supports-color@7.2.0) - "@babel/types": 7.29.8 - "@jridgewell/remapping": 2.3.5 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) + '@babel/helpers': 7.29.10 + '@babel/parser': 7.29.9 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.10(supports-color@7.2.0) + '@babel/types': 7.29.8 + '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3(supports-color@7.2.0) gensync: 1.0.0-beta.2 @@ -5761,310 +3695,310 @@ snapshots: transitivePeerDependencies: - supports-color - "@babel/generator@7.29.8": + '@babel/generator@7.29.8': dependencies: - "@babel/parser": 7.29.9 - "@babel/types": 7.29.8 - "@jridgewell/gen-mapping": 0.3.13 - "@jridgewell/trace-mapping": 0.3.31 + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 - "@babel/helper-compilation-targets@7.29.7": + '@babel/helper-compilation-targets@7.29.7': dependencies: - "@babel/compat-data": 7.29.7 - "@babel/helper-validator-option": 7.29.7 + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 browserslist: 4.29.4 lru-cache: 5.1.1 semver: 6.3.1 - "@babel/helper-globals@7.29.7": {} + '@babel/helper-globals@7.29.7': {} - "@babel/helper-module-imports@7.29.7(supports-color@7.2.0)": + '@babel/helper-module-imports@7.29.7(supports-color@7.2.0)': dependencies: - "@babel/traverse": 7.29.10(supports-color@7.2.0) - "@babel/types": 7.29.8 + '@babel/traverse': 7.29.10(supports-color@7.2.0) + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color - "@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0)": + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0)': dependencies: - "@babel/core": 7.29.7(supports-color@7.2.0) - "@babel/helper-module-imports": 7.29.7(supports-color@7.2.0) - "@babel/helper-validator-identifier": 7.29.7 - "@babel/traverse": 7.29.10(supports-color@7.2.0) + '@babel/core': 7.29.7(supports-color@7.2.0) + '@babel/helper-module-imports': 7.29.7(supports-color@7.2.0) + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.10(supports-color@7.2.0) transitivePeerDependencies: - supports-color - "@babel/helper-plugin-utils@7.29.7": {} + '@babel/helper-plugin-utils@7.29.7': {} - "@babel/helper-string-parser@7.29.7": {} + '@babel/helper-string-parser@7.29.7': {} - "@babel/helper-validator-identifier@7.29.7": {} + '@babel/helper-validator-identifier@7.29.7': {} - "@babel/helper-validator-option@7.29.7": {} + '@babel/helper-validator-option@7.29.7': {} - "@babel/helpers@7.29.10": + '@babel/helpers@7.29.10': dependencies: - "@babel/template": 7.29.7 - "@babel/types": 7.29.8 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 - "@babel/parser@7.29.9": + '@babel/parser@7.29.9': dependencies: - "@babel/types": 7.29.8 + '@babel/types': 7.29.8 - "@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))": + '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))': dependencies: - "@babel/core": 7.29.7(supports-color@7.2.0) - "@babel/helper-plugin-utils": 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) + '@babel/helper-plugin-utils': 7.29.7 - "@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))": + '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))': dependencies: - "@babel/core": 7.29.7(supports-color@7.2.0) - "@babel/helper-plugin-utils": 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) + '@babel/helper-plugin-utils': 7.29.7 - "@babel/template@7.29.7": + '@babel/template@7.29.7': dependencies: - "@babel/code-frame": 7.29.7 - "@babel/parser": 7.29.9 - "@babel/types": 7.29.8 + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 - "@babel/traverse@7.29.10(supports-color@7.2.0)": + '@babel/traverse@7.29.10(supports-color@7.2.0)': dependencies: - "@babel/code-frame": 7.29.7 - "@babel/generator": 7.29.8 - "@babel/helper-globals": 7.29.7 - "@babel/parser": 7.29.9 - "@babel/template": 7.29.7 - "@babel/types": 7.29.8 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 debug: 4.4.3(supports-color@7.2.0) transitivePeerDependencies: - supports-color - "@babel/types@7.29.8": + '@babel/types@7.29.8': dependencies: - "@babel/helper-string-parser": 7.29.7 - "@babel/helper-validator-identifier": 7.29.7 + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 - "@capsizecss/unpack@4.0.1": + '@capsizecss/unpack@4.0.1': dependencies: fontkitten: 1.0.3 - "@emmetio/abbreviation@2.3.3": + '@emmetio/abbreviation@2.3.3': dependencies: - "@emmetio/scanner": 1.0.4 + '@emmetio/scanner': 1.0.4 - "@emmetio/css-abbreviation@2.1.8": + '@emmetio/css-abbreviation@2.1.8': dependencies: - "@emmetio/scanner": 1.0.4 + '@emmetio/scanner': 1.0.4 - "@emmetio/css-parser@0.4.1": + '@emmetio/css-parser@0.4.1': dependencies: - "@emmetio/stream-reader": 2.2.0 - "@emmetio/stream-reader-utils": 0.1.0 + '@emmetio/stream-reader': 2.2.0 + '@emmetio/stream-reader-utils': 0.1.0 - "@emmetio/html-matcher@1.3.0": + '@emmetio/html-matcher@1.3.0': dependencies: - "@emmetio/scanner": 1.0.4 + '@emmetio/scanner': 1.0.4 - "@emmetio/scanner@1.0.4": {} + '@emmetio/scanner@1.0.4': {} - "@emmetio/stream-reader-utils@0.1.0": {} + '@emmetio/stream-reader-utils@0.1.0': {} - "@emmetio/stream-reader@2.2.0": {} + '@emmetio/stream-reader@2.2.0': {} - "@emnapi/core@1.11.3": + '@emnapi/core@1.11.3': dependencies: - "@emnapi/wasi-threads": 1.2.3 + '@emnapi/wasi-threads': 1.2.3 tslib: 2.8.1 - "@emnapi/runtime@1.11.3": + '@emnapi/runtime@1.11.3': dependencies: tslib: 2.8.1 - "@emnapi/wasi-threads@1.2.3": + '@emnapi/wasi-threads@1.2.3': dependencies: tslib: 2.8.1 - "@esbuild/aix-ppc64@0.25.12": + '@esbuild/aix-ppc64@0.25.12': optional: true - "@esbuild/aix-ppc64@0.27.7": + '@esbuild/aix-ppc64@0.27.7': optional: true - "@esbuild/android-arm64@0.25.12": + '@esbuild/android-arm64@0.25.12': optional: true - "@esbuild/android-arm64@0.27.7": + '@esbuild/android-arm64@0.27.7': optional: true - "@esbuild/android-arm@0.25.12": + '@esbuild/android-arm@0.25.12': optional: true - "@esbuild/android-arm@0.27.7": + '@esbuild/android-arm@0.27.7': optional: true - "@esbuild/android-x64@0.25.12": + '@esbuild/android-x64@0.25.12': optional: true - "@esbuild/android-x64@0.27.7": + '@esbuild/android-x64@0.27.7': optional: true - "@esbuild/darwin-arm64@0.25.12": + '@esbuild/darwin-arm64@0.25.12': optional: true - "@esbuild/darwin-arm64@0.27.7": + '@esbuild/darwin-arm64@0.27.7': optional: true - "@esbuild/darwin-x64@0.25.12": + '@esbuild/darwin-x64@0.25.12': optional: true - "@esbuild/darwin-x64@0.27.7": + '@esbuild/darwin-x64@0.27.7': optional: true - "@esbuild/freebsd-arm64@0.25.12": + '@esbuild/freebsd-arm64@0.25.12': optional: true - "@esbuild/freebsd-arm64@0.27.7": + '@esbuild/freebsd-arm64@0.27.7': optional: true - "@esbuild/freebsd-x64@0.25.12": + '@esbuild/freebsd-x64@0.25.12': optional: true - "@esbuild/freebsd-x64@0.27.7": + '@esbuild/freebsd-x64@0.27.7': optional: true - "@esbuild/linux-arm64@0.25.12": + '@esbuild/linux-arm64@0.25.12': optional: true - "@esbuild/linux-arm64@0.27.7": + '@esbuild/linux-arm64@0.27.7': optional: true - "@esbuild/linux-arm@0.25.12": + '@esbuild/linux-arm@0.25.12': optional: true - "@esbuild/linux-arm@0.27.7": + '@esbuild/linux-arm@0.27.7': optional: true - "@esbuild/linux-ia32@0.25.12": + '@esbuild/linux-ia32@0.25.12': optional: true - "@esbuild/linux-ia32@0.27.7": + '@esbuild/linux-ia32@0.27.7': optional: true - "@esbuild/linux-loong64@0.25.12": + '@esbuild/linux-loong64@0.25.12': optional: true - "@esbuild/linux-loong64@0.27.7": + '@esbuild/linux-loong64@0.27.7': optional: true - "@esbuild/linux-mips64el@0.25.12": + '@esbuild/linux-mips64el@0.25.12': optional: true - "@esbuild/linux-mips64el@0.27.7": + '@esbuild/linux-mips64el@0.27.7': optional: true - "@esbuild/linux-ppc64@0.25.12": + '@esbuild/linux-ppc64@0.25.12': optional: true - "@esbuild/linux-ppc64@0.27.7": + '@esbuild/linux-ppc64@0.27.7': optional: true - "@esbuild/linux-riscv64@0.25.12": + '@esbuild/linux-riscv64@0.25.12': optional: true - "@esbuild/linux-riscv64@0.27.7": + '@esbuild/linux-riscv64@0.27.7': optional: true - "@esbuild/linux-s390x@0.25.12": + '@esbuild/linux-s390x@0.25.12': optional: true - "@esbuild/linux-s390x@0.27.7": + '@esbuild/linux-s390x@0.27.7': optional: true - "@esbuild/linux-x64@0.25.12": + '@esbuild/linux-x64@0.25.12': optional: true - "@esbuild/linux-x64@0.27.7": + '@esbuild/linux-x64@0.27.7': optional: true - "@esbuild/netbsd-arm64@0.25.12": + '@esbuild/netbsd-arm64@0.25.12': optional: true - "@esbuild/netbsd-arm64@0.27.7": + '@esbuild/netbsd-arm64@0.27.7': optional: true - "@esbuild/netbsd-x64@0.25.12": + '@esbuild/netbsd-x64@0.25.12': optional: true - "@esbuild/netbsd-x64@0.27.7": + '@esbuild/netbsd-x64@0.27.7': optional: true - "@esbuild/openbsd-arm64@0.25.12": + '@esbuild/openbsd-arm64@0.25.12': optional: true - "@esbuild/openbsd-arm64@0.27.7": + '@esbuild/openbsd-arm64@0.27.7': optional: true - "@esbuild/openbsd-x64@0.25.12": + '@esbuild/openbsd-x64@0.25.12': optional: true - "@esbuild/openbsd-x64@0.27.7": + '@esbuild/openbsd-x64@0.27.7': optional: true - "@esbuild/openharmony-arm64@0.25.12": + '@esbuild/openharmony-arm64@0.25.12': optional: true - "@esbuild/openharmony-arm64@0.27.7": + '@esbuild/openharmony-arm64@0.27.7': optional: true - "@esbuild/sunos-x64@0.25.12": + '@esbuild/sunos-x64@0.25.12': optional: true - "@esbuild/sunos-x64@0.27.7": + '@esbuild/sunos-x64@0.27.7': optional: true - "@esbuild/win32-arm64@0.25.12": + '@esbuild/win32-arm64@0.25.12': optional: true - "@esbuild/win32-arm64@0.27.7": + '@esbuild/win32-arm64@0.27.7': optional: true - "@esbuild/win32-ia32@0.25.12": + '@esbuild/win32-ia32@0.25.12': optional: true - "@esbuild/win32-ia32@0.27.7": + '@esbuild/win32-ia32@0.27.7': optional: true - "@esbuild/win32-x64@0.25.12": + '@esbuild/win32-x64@0.25.12': optional: true - "@esbuild/win32-x64@0.27.7": + '@esbuild/win32-x64@0.27.7': optional: true - "@eslint-community/eslint-utils@4.10.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))": + '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))': dependencies: eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) eslint-visitor-keys: 3.4.3 - "@eslint-community/regexpp@4.12.2": {} + '@eslint-community/regexpp@4.12.2': {} - "@eslint/config-array@0.21.2(supports-color@7.2.0)": + '@eslint/config-array@0.21.2(supports-color@7.2.0)': dependencies: - "@eslint/object-schema": 2.1.7 + '@eslint/object-schema': 2.1.7 debug: 4.4.3(supports-color@7.2.0) minimatch: 3.1.5 transitivePeerDependencies: - supports-color - "@eslint/config-helpers@0.4.2": + '@eslint/config-helpers@0.4.2': dependencies: - "@eslint/core": 0.17.0 + '@eslint/core': 0.17.0 - "@eslint/core@0.17.0": + '@eslint/core@0.17.0': dependencies: - "@types/json-schema": 7.0.15 + '@types/json-schema': 7.0.15 - "@eslint/eslintrc@3.3.7(supports-color@7.2.0)": + '@eslint/eslintrc@3.3.7(supports-color@7.2.0)': dependencies: ajv: 6.15.0 debug: 4.4.3(supports-color@7.2.0) @@ -6078,281 +4012,285 @@ snapshots: transitivePeerDependencies: - supports-color - "@eslint/js@9.39.5": {} + '@eslint/js@9.39.5': {} - "@eslint/object-schema@2.1.7": {} + '@eslint/object-schema@2.1.7': {} - "@eslint/plugin-kit@0.4.1": + '@eslint/plugin-kit@0.4.1': dependencies: - "@eslint/core": 0.17.0 + '@eslint/core': 0.17.0 levn: 0.4.1 - "@fontsource-variable/inter@5.3.0": {} + '@fontsource-variable/inter@5.3.0': {} - "@humanfs/core@0.19.2": + '@humanfs/core@0.19.2': dependencies: - "@humanfs/types": 0.15.0 + '@humanfs/types': 0.15.0 - "@humanfs/node@0.16.8": + '@humanfs/node@0.16.8': dependencies: - "@humanfs/core": 0.19.2 - "@humanfs/types": 0.15.0 - "@humanwhocodes/retry": 0.4.3 + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 - "@humanfs/types@0.15.0": {} + '@humanfs/types@0.15.0': {} - "@humanwhocodes/module-importer@1.0.1": {} + '@humanwhocodes/module-importer@1.0.1': {} - "@humanwhocodes/retry@0.4.3": {} + '@humanwhocodes/retry@0.4.3': {} - "@img/colour@1.1.0": + '@img/colour@1.1.0': optional: true - "@img/sharp-darwin-arm64@0.34.5": + '@img/sharp-darwin-arm64@0.34.5': optionalDependencies: - "@img/sharp-libvips-darwin-arm64": 1.2.4 + '@img/sharp-libvips-darwin-arm64': 1.2.4 optional: true - "@img/sharp-darwin-x64@0.34.5": + '@img/sharp-darwin-x64@0.34.5': optionalDependencies: - "@img/sharp-libvips-darwin-x64": 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 optional: true - "@img/sharp-libvips-darwin-arm64@1.2.4": + '@img/sharp-libvips-darwin-arm64@1.2.4': optional: true - "@img/sharp-libvips-darwin-x64@1.2.4": + '@img/sharp-libvips-darwin-x64@1.2.4': optional: true - "@img/sharp-libvips-linux-arm64@1.2.4": + '@img/sharp-libvips-linux-arm64@1.2.4': optional: true - "@img/sharp-libvips-linux-arm@1.2.4": + '@img/sharp-libvips-linux-arm@1.2.4': optional: true - "@img/sharp-libvips-linux-ppc64@1.2.4": + '@img/sharp-libvips-linux-ppc64@1.2.4': optional: true - "@img/sharp-libvips-linux-riscv64@1.2.4": + '@img/sharp-libvips-linux-riscv64@1.2.4': optional: true - "@img/sharp-libvips-linux-s390x@1.2.4": + '@img/sharp-libvips-linux-s390x@1.2.4': optional: true - "@img/sharp-libvips-linux-x64@1.2.4": + '@img/sharp-libvips-linux-x64@1.2.4': optional: true - "@img/sharp-libvips-linuxmusl-arm64@1.2.4": + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': optional: true - "@img/sharp-libvips-linuxmusl-x64@1.2.4": + '@img/sharp-libvips-linuxmusl-x64@1.2.4': optional: true - "@img/sharp-linux-arm64@0.34.5": + '@img/sharp-linux-arm64@0.34.5': optionalDependencies: - "@img/sharp-libvips-linux-arm64": 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 optional: true - "@img/sharp-linux-arm@0.34.5": + '@img/sharp-linux-arm@0.34.5': optionalDependencies: - "@img/sharp-libvips-linux-arm": 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 optional: true - "@img/sharp-linux-ppc64@0.34.5": + '@img/sharp-linux-ppc64@0.34.5': optionalDependencies: - "@img/sharp-libvips-linux-ppc64": 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 optional: true - "@img/sharp-linux-riscv64@0.34.5": + '@img/sharp-linux-riscv64@0.34.5': optionalDependencies: - "@img/sharp-libvips-linux-riscv64": 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 optional: true - "@img/sharp-linux-s390x@0.34.5": + '@img/sharp-linux-s390x@0.34.5': optionalDependencies: - "@img/sharp-libvips-linux-s390x": 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 optional: true - "@img/sharp-linux-x64@0.34.5": + '@img/sharp-linux-x64@0.34.5': optionalDependencies: - "@img/sharp-libvips-linux-x64": 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 optional: true - "@img/sharp-linuxmusl-arm64@0.34.5": + '@img/sharp-linuxmusl-arm64@0.34.5': optionalDependencies: - "@img/sharp-libvips-linuxmusl-arm64": 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 optional: true - "@img/sharp-linuxmusl-x64@0.34.5": + '@img/sharp-linuxmusl-x64@0.34.5': optionalDependencies: - "@img/sharp-libvips-linuxmusl-x64": 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 optional: true - "@img/sharp-wasm32@0.34.5": + '@img/sharp-wasm32@0.34.5': dependencies: - "@emnapi/runtime": 1.11.3 + '@emnapi/runtime': 1.11.3 optional: true - "@img/sharp-win32-arm64@0.34.5": + '@img/sharp-win32-arm64@0.34.5': optional: true - "@img/sharp-win32-ia32@0.34.5": + '@img/sharp-win32-ia32@0.34.5': optional: true - "@img/sharp-win32-x64@0.34.5": + '@img/sharp-win32-x64@0.34.5': optional: true - "@jridgewell/gen-mapping@0.3.13": + '@jridgewell/gen-mapping@0.3.13': dependencies: - "@jridgewell/sourcemap-codec": 1.6.0 - "@jridgewell/trace-mapping": 0.3.31 + '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/trace-mapping': 0.3.31 - "@jridgewell/remapping@2.3.5": + '@jridgewell/remapping@2.3.5': dependencies: - "@jridgewell/gen-mapping": 0.3.13 - "@jridgewell/trace-mapping": 0.3.31 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 - "@jridgewell/resolve-uri@3.1.2": {} + '@jridgewell/resolve-uri@3.1.2': {} - "@jridgewell/sourcemap-codec@1.6.0": {} + '@jridgewell/sourcemap-codec@1.6.0': {} - "@jridgewell/trace-mapping@0.3.31": + '@jridgewell/trace-mapping@0.3.31': dependencies: - "@jridgewell/resolve-uri": 3.1.2 - "@jridgewell/sourcemap-codec": 1.6.0 + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.6.0 - "@napi-rs/lzma-linux-x64-gnu@1.5.1": + '@napi-rs/lzma-linux-x64-gnu@1.5.1': optional: true - "@napi-rs/wasm-runtime@1.2.4(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)": + '@napi-rs/wasm-runtime@1.2.4(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)': dependencies: - "@emnapi/core": 1.11.3 - "@emnapi/runtime": 1.11.3 - "@tybys/wasm-util": 0.10.4 + '@emnapi/core': 1.11.3 + '@emnapi/runtime': 1.11.3 + '@tybys/wasm-util': 0.10.4 - "@oslojs/encoding@1.1.0": {} + '@oslojs/encoding@1.1.0': {} + + '@playwright/test@1.56.1': + dependencies: + playwright: 1.56.1 - "@rolldown/pluginutils@1.0.0-beta.27": {} + '@rolldown/pluginutils@1.0.0-beta.27': {} - "@rollup/pluginutils@5.5.1(rollup@4.65.0)": + '@rollup/pluginutils@5.5.1(rollup@4.65.0)': dependencies: - "@types/estree": 1.0.9 + '@types/estree': 1.0.9 estree-walker: 2.0.2 picomatch: 4.0.7 optionalDependencies: rollup: 4.65.0 - "@rollup/rollup-android-arm-eabi@4.65.0": + '@rollup/rollup-android-arm-eabi@4.65.0': optional: true - "@rollup/rollup-android-arm64@4.65.0": + '@rollup/rollup-android-arm64@4.65.0': optional: true - "@rollup/rollup-darwin-arm64@4.65.0": + '@rollup/rollup-darwin-arm64@4.65.0': optional: true - "@rollup/rollup-darwin-x64@4.65.0": + '@rollup/rollup-darwin-x64@4.65.0': optional: true - "@rollup/rollup-freebsd-arm64@4.65.0": + '@rollup/rollup-freebsd-arm64@4.65.0': optional: true - "@rollup/rollup-freebsd-x64@4.65.0": + '@rollup/rollup-freebsd-x64@4.65.0': optional: true - "@rollup/rollup-linux-arm-gnueabihf@4.65.0": + '@rollup/rollup-linux-arm-gnueabihf@4.65.0': optional: true - "@rollup/rollup-linux-arm-musleabihf@4.65.0": + '@rollup/rollup-linux-arm-musleabihf@4.65.0': optional: true - "@rollup/rollup-linux-arm64-gnu@4.65.0": + '@rollup/rollup-linux-arm64-gnu@4.65.0': optional: true - "@rollup/rollup-linux-arm64-musl@4.65.0": + '@rollup/rollup-linux-arm64-musl@4.65.0': optional: true - "@rollup/rollup-linux-loong64-gnu@4.65.0": + '@rollup/rollup-linux-loong64-gnu@4.65.0': optional: true - "@rollup/rollup-linux-loong64-musl@4.65.0": + '@rollup/rollup-linux-loong64-musl@4.65.0': optional: true - "@rollup/rollup-linux-ppc64-gnu@4.65.0": + '@rollup/rollup-linux-ppc64-gnu@4.65.0': optional: true - "@rollup/rollup-linux-ppc64-musl@4.65.0": + '@rollup/rollup-linux-ppc64-musl@4.65.0': optional: true - "@rollup/rollup-linux-riscv64-gnu@4.65.0": + '@rollup/rollup-linux-riscv64-gnu@4.65.0': optional: true - "@rollup/rollup-linux-riscv64-musl@4.65.0": + '@rollup/rollup-linux-riscv64-musl@4.65.0': optional: true - "@rollup/rollup-linux-s390x-gnu@4.65.0": + '@rollup/rollup-linux-s390x-gnu@4.65.0': optional: true - "@rollup/rollup-linux-x64-gnu@4.65.0": + '@rollup/rollup-linux-x64-gnu@4.65.0': optional: true - "@rollup/rollup-linux-x64-musl@4.65.0": + '@rollup/rollup-linux-x64-musl@4.65.0': optional: true - "@rollup/rollup-openbsd-x64@4.65.0": + '@rollup/rollup-openbsd-x64@4.65.0': optional: true - "@rollup/rollup-openharmony-arm64@4.65.0": + '@rollup/rollup-openharmony-arm64@4.65.0': optional: true - "@rollup/rollup-win32-arm64-msvc@4.65.0": + '@rollup/rollup-win32-arm64-msvc@4.65.0': optional: true - "@rollup/rollup-win32-ia32-msvc@4.65.0": + '@rollup/rollup-win32-ia32-msvc@4.65.0': optional: true - "@rollup/rollup-win32-x64-gnu@4.65.0": + '@rollup/rollup-win32-x64-gnu@4.65.0': optional: true - "@rollup/rollup-win32-x64-msvc@4.65.0": + '@rollup/rollup-win32-x64-msvc@4.65.0': optional: true - "@shikijs/core@3.23.0": + '@shikijs/core@3.23.0': dependencies: - "@shikijs/types": 3.23.0 - "@shikijs/vscode-textmate": 10.1.0 - "@types/hast": 3.0.5 + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.1.0 + '@types/hast': 3.0.5 hast-util-to-html: 9.0.5 - "@shikijs/engine-javascript@3.23.0": + '@shikijs/engine-javascript@3.23.0': dependencies: - "@shikijs/types": 3.23.0 - "@shikijs/vscode-textmate": 10.1.0 + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.1.0 oniguruma-to-es: 4.3.6 - "@shikijs/engine-oniguruma@3.23.0": + '@shikijs/engine-oniguruma@3.23.0': dependencies: - "@shikijs/types": 3.23.0 - "@shikijs/vscode-textmate": 10.1.0 + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.1.0 - "@shikijs/langs@3.23.0": + '@shikijs/langs@3.23.0': dependencies: - "@shikijs/types": 3.23.0 + '@shikijs/types': 3.23.0 - "@shikijs/themes@3.23.0": + '@shikijs/themes@3.23.0': dependencies: - "@shikijs/types": 3.23.0 + '@shikijs/types': 3.23.0 - "@shikijs/types@3.23.0": + '@shikijs/types@3.23.0': dependencies: - "@shikijs/vscode-textmate": 10.1.0 - "@types/hast": 3.0.5 + '@shikijs/vscode-textmate': 10.1.0 + '@types/hast': 3.0.5 - "@shikijs/vscode-textmate@10.1.0": {} + '@shikijs/vscode-textmate@10.1.0': {} - "@tailwindcss/node@4.3.3": + '@tailwindcss/node@4.3.3': dependencies: - "@jridgewell/remapping": 2.3.5 + '@jridgewell/remapping': 2.3.5 enhanced-resolve: 5.26.0 jiti: 2.7.0 lightningcss: 1.32.0 @@ -6360,132 +4298,139 @@ snapshots: source-map-js: 1.2.2 tailwindcss: 4.3.3 - "@tailwindcss/oxide-android-arm64@4.3.3": + '@tailwindcss/oxide-android-arm64@4.3.3': optional: true - "@tailwindcss/oxide-darwin-arm64@4.3.3": + '@tailwindcss/oxide-darwin-arm64@4.3.3': optional: true - "@tailwindcss/oxide-darwin-x64@4.3.3": + '@tailwindcss/oxide-darwin-x64@4.3.3': optional: true - "@tailwindcss/oxide-freebsd-x64@4.3.3": + '@tailwindcss/oxide-freebsd-x64@4.3.3': optional: true - "@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3": + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': optional: true - "@tailwindcss/oxide-linux-arm64-gnu@4.3.3": + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': optional: true - "@tailwindcss/oxide-linux-arm64-musl@4.3.3": + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': optional: true - "@tailwindcss/oxide-linux-x64-gnu@4.3.3": + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': optional: true - "@tailwindcss/oxide-linux-x64-musl@4.3.3": + '@tailwindcss/oxide-linux-x64-musl@4.3.3': optional: true - "@tailwindcss/oxide-wasm32-wasi@4.3.3": + '@tailwindcss/oxide-wasm32-wasi@4.3.3': optional: true - "@tailwindcss/oxide-win32-arm64-msvc@4.3.3": + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': optional: true - "@tailwindcss/oxide-win32-x64-msvc@4.3.3": + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': optional: true - "@tailwindcss/oxide@4.3.3": + '@tailwindcss/oxide@4.3.3': optionalDependencies: - "@tailwindcss/oxide-android-arm64": 4.3.3 - "@tailwindcss/oxide-darwin-arm64": 4.3.3 - "@tailwindcss/oxide-darwin-x64": 4.3.3 - "@tailwindcss/oxide-freebsd-x64": 4.3.3 - "@tailwindcss/oxide-linux-arm-gnueabihf": 4.3.3 - "@tailwindcss/oxide-linux-arm64-gnu": 4.3.3 - "@tailwindcss/oxide-linux-arm64-musl": 4.3.3 - "@tailwindcss/oxide-linux-x64-gnu": 4.3.3 - "@tailwindcss/oxide-linux-x64-musl": 4.3.3 - "@tailwindcss/oxide-wasm32-wasi": 4.3.3 - "@tailwindcss/oxide-win32-arm64-msvc": 4.3.3 - "@tailwindcss/oxide-win32-x64-msvc": 4.3.3 - - "@tailwindcss/vite@4.3.3(vite@6.4.5(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.1))": - dependencies: - "@tailwindcss/node": 4.3.3 - "@tailwindcss/oxide": 4.3.3 + '@tailwindcss/oxide-android-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-x64': 4.3.3 + '@tailwindcss/oxide-freebsd-x64': 4.3.3 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.3 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.3 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-x64-musl': 4.3.3 + '@tailwindcss/oxide-wasm32-wasi': 4.3.3 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.3 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.3 + + '@tailwindcss/vite@4.3.3(vite@6.4.5(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.1))': + dependencies: + '@tailwindcss/node': 4.3.3 + '@tailwindcss/oxide': 4.3.3 tailwindcss: 4.3.3 vite: 6.4.5(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.1) - "@tybys/wasm-util@0.10.4": + '@tybys/wasm-util@0.10.4': dependencies: tslib: 2.8.1 - "@types/babel__core@7.20.5": + '@types/babel__core@7.20.5': dependencies: - "@babel/parser": 7.29.9 - "@babel/types": 7.29.8 - "@types/babel__generator": 7.27.0 - "@types/babel__template": 7.4.4 - "@types/babel__traverse": 7.28.0 + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 - "@types/babel__generator@7.27.0": + '@types/babel__generator@7.27.0': dependencies: - "@babel/types": 7.29.8 + '@babel/types': 7.29.8 - "@types/babel__template@7.4.4": + '@types/babel__template@7.4.4': dependencies: - "@babel/parser": 7.29.9 - "@babel/types": 7.29.8 + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 - "@types/babel__traverse@7.28.0": + '@types/babel__traverse@7.28.0': dependencies: - "@babel/types": 7.29.8 + '@babel/types': 7.29.8 - "@types/debug@4.1.13": + '@types/chai@5.2.3': dependencies: - "@types/ms": 2.1.0 + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 - "@types/estree@1.0.9": {} + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + + '@types/deep-eql@4.0.2': {} - "@types/hast@3.0.5": + '@types/estree@1.0.9': {} + + '@types/hast@3.0.5': dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 - "@types/json-schema@7.0.15": {} + '@types/json-schema@7.0.15': {} - "@types/mdast@4.0.4": + '@types/mdast@4.0.4': dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 - "@types/ms@2.1.0": {} + '@types/ms@2.1.0': {} - "@types/nlcst@2.0.3": + '@types/nlcst@2.0.3': dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 - "@types/prop-types@15.7.15": {} + '@types/prop-types@15.7.15': {} - "@types/react-dom@18.3.7(@types/react@18.3.31)": + '@types/react-dom@18.3.7(@types/react@18.3.31)': dependencies: - "@types/react": 18.3.31 + '@types/react': 18.3.31 - "@types/react@18.3.31": + '@types/react@18.3.31': dependencies: - "@types/prop-types": 15.7.15 + '@types/prop-types': 15.7.15 csstype: 3.2.3 - "@types/unist@3.0.3": {} + '@types/unist@3.0.3': {} - "@typescript-eslint/eslint-plugin@8.72.0(@typescript-eslint/parser@8.72.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)": + '@typescript-eslint/eslint-plugin@8.72.0(@typescript-eslint/parser@8.72.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)': dependencies: - "@eslint-community/regexpp": 4.12.2 - "@typescript-eslint/parser": 8.72.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) - "@typescript-eslint/scope-manager": 8.72.0 - "@typescript-eslint/type-utils": 8.72.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) - "@typescript-eslint/utils": 8.72.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) - "@typescript-eslint/visitor-keys": 8.72.0 + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.72.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.72.0 + '@typescript-eslint/type-utils': 8.72.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/utils': 8.72.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.72.0 eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) ignore: 7.0.13 natural-compare: 1.4.0 @@ -6494,41 +4439,41 @@ snapshots: transitivePeerDependencies: - supports-color - "@typescript-eslint/parser@8.72.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)": + '@typescript-eslint/parser@8.72.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)': dependencies: - "@typescript-eslint/scope-manager": 8.72.0 - "@typescript-eslint/types": 8.72.0 - "@typescript-eslint/typescript-estree": 8.72.0(supports-color@7.2.0)(typescript@5.9.3) - "@typescript-eslint/visitor-keys": 8.72.0 + '@typescript-eslint/scope-manager': 8.72.0 + '@typescript-eslint/types': 8.72.0 + '@typescript-eslint/typescript-estree': 8.72.0(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.72.0 debug: 4.4.3(supports-color@7.2.0) eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) typescript: 5.9.3 transitivePeerDependencies: - supports-color - "@typescript-eslint/project-service@8.72.0(supports-color@7.2.0)(typescript@5.9.3)": + '@typescript-eslint/project-service@8.72.0(supports-color@7.2.0)(typescript@5.9.3)': dependencies: - "@typescript-eslint/tsconfig-utils": 8.72.0(typescript@5.9.3) - "@typescript-eslint/types": 8.72.0 + '@typescript-eslint/tsconfig-utils': 8.72.0(typescript@5.9.3) + '@typescript-eslint/types': 8.72.0 debug: 4.4.3(supports-color@7.2.0) typescript: 5.9.3 transitivePeerDependencies: - supports-color - "@typescript-eslint/scope-manager@8.72.0": + '@typescript-eslint/scope-manager@8.72.0': dependencies: - "@typescript-eslint/types": 8.72.0 - "@typescript-eslint/visitor-keys": 8.72.0 + '@typescript-eslint/types': 8.72.0 + '@typescript-eslint/visitor-keys': 8.72.0 - "@typescript-eslint/tsconfig-utils@8.72.0(typescript@5.9.3)": + '@typescript-eslint/tsconfig-utils@8.72.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - "@typescript-eslint/type-utils@8.72.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)": + '@typescript-eslint/type-utils@8.72.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)': dependencies: - "@typescript-eslint/types": 8.72.0 - "@typescript-eslint/typescript-estree": 8.72.0(supports-color@7.2.0)(typescript@5.9.3) - "@typescript-eslint/utils": 8.72.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/types': 8.72.0 + '@typescript-eslint/typescript-estree': 8.72.0(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/utils': 8.72.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) debug: 4.4.3(supports-color@7.2.0) eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) ts-api-utils: 2.5.0(typescript@5.9.3) @@ -6536,14 +4481,14 @@ snapshots: transitivePeerDependencies: - supports-color - "@typescript-eslint/types@8.72.0": {} + '@typescript-eslint/types@8.72.0': {} - "@typescript-eslint/typescript-estree@8.72.0(supports-color@7.2.0)(typescript@5.9.3)": + '@typescript-eslint/typescript-estree@8.72.0(supports-color@7.2.0)(typescript@5.9.3)': dependencies: - "@typescript-eslint/project-service": 8.72.0(supports-color@7.2.0)(typescript@5.9.3) - "@typescript-eslint/tsconfig-utils": 8.72.0(typescript@5.9.3) - "@typescript-eslint/types": 8.72.0 - "@typescript-eslint/visitor-keys": 8.72.0 + '@typescript-eslint/project-service': 8.72.0(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.72.0(typescript@5.9.3) + '@typescript-eslint/types': 8.72.0 + '@typescript-eslint/visitor-keys': 8.72.0 debug: 4.4.3(supports-color@7.2.0) minimatch: 10.2.6 semver: 7.8.5 @@ -6553,54 +4498,100 @@ snapshots: transitivePeerDependencies: - supports-color - "@typescript-eslint/utils@8.72.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)": + '@typescript-eslint/utils@8.72.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)': dependencies: - "@eslint-community/eslint-utils": 4.10.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) - "@typescript-eslint/scope-manager": 8.72.0 - "@typescript-eslint/types": 8.72.0 - "@typescript-eslint/typescript-estree": 8.72.0(supports-color@7.2.0)(typescript@5.9.3) + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) + '@typescript-eslint/scope-manager': 8.72.0 + '@typescript-eslint/types': 8.72.0 + '@typescript-eslint/typescript-estree': 8.72.0(supports-color@7.2.0)(typescript@5.9.3) eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) typescript: 5.9.3 transitivePeerDependencies: - supports-color - "@typescript-eslint/visitor-keys@8.72.0": + '@typescript-eslint/visitor-keys@8.72.0': dependencies: - "@typescript-eslint/types": 8.72.0 + '@typescript-eslint/types': 8.72.0 eslint-visitor-keys: 5.0.1 - "@ungap/structured-clone@1.4.0": {} + '@ungap/structured-clone@1.4.0': {} - "@vitejs/plugin-react@4.7.0(supports-color@7.2.0)(vite@6.4.5(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.1))": + '@vitejs/plugin-react@4.7.0(supports-color@7.2.0)(vite@6.4.5(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.1))': dependencies: - "@babel/core": 7.29.7(supports-color@7.2.0) - "@babel/plugin-transform-react-jsx-self": 7.29.7(@babel/core@7.29.7(supports-color@7.2.0)) - "@babel/plugin-transform-react-jsx-source": 7.29.7(@babel/core@7.29.7(supports-color@7.2.0)) - "@rolldown/pluginutils": 1.0.0-beta.27 - "@types/babel__core": 7.20.5 + '@babel/core': 7.29.7(supports-color@7.2.0) + '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0)) + '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0)) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 react-refresh: 0.17.0 vite: 6.4.5(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.1) transitivePeerDependencies: - supports-color - "@volar/kit@2.4.30(typescript@5.9.3)": + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.3 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.4(vite@6.4.5(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.1))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 6.4.5(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.1) + + '@vitest/pretty-format@3.2.4': dependencies: - "@volar/language-service": 2.4.30(typescript@5.9.3) - "@volar/typescript": 2.4.30(typescript@5.9.3) + tinyrainbow: 2.0.0 + + '@vitest/pretty-format@3.2.7': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.2.4': + dependencies: + '@vitest/utils': 3.2.4 + pathe: 2.0.3 + strip-literal: 3.1.0 + + '@vitest/snapshot@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.6 + + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + + '@volar/kit@2.4.30(typescript@5.9.3)': + dependencies: + '@volar/language-service': 2.4.30(typescript@5.9.3) + '@volar/typescript': 2.4.30(typescript@5.9.3) typesafe-path: 0.2.2 typescript: 5.9.3 vscode-languageserver-textdocument: 1.0.16 vscode-uri: 3.2.0 - "@volar/language-core@2.4.30": + '@volar/language-core@2.4.30': dependencies: - "@volar/source-map": 2.4.30 + '@volar/source-map': 2.4.30 - "@volar/language-server@2.4.30(typescript@5.9.3)": + '@volar/language-server@2.4.30(typescript@5.9.3)': dependencies: - "@volar/language-core": 2.4.30 - "@volar/language-service": 2.4.30(typescript@5.9.3) - "@volar/typescript": 2.4.30(typescript@5.9.3) + '@volar/language-core': 2.4.30 + '@volar/language-service': 2.4.30(typescript@5.9.3) + '@volar/typescript': 2.4.30(typescript@5.9.3) path-browserify: 1.0.1 request-light: 0.7.0 vscode-languageserver: 9.0.1 @@ -6610,27 +4601,27 @@ snapshots: optionalDependencies: typescript: 5.9.3 - "@volar/language-service@2.4.30(typescript@5.9.3)": + '@volar/language-service@2.4.30(typescript@5.9.3)': dependencies: - "@volar/language-core": 2.4.30 + '@volar/language-core': 2.4.30 vscode-languageserver-protocol: 3.18.5 vscode-languageserver-textdocument: 1.0.16 vscode-uri: 3.2.0 optionalDependencies: typescript: 5.9.3 - "@volar/source-map@2.4.30": {} + '@volar/source-map@2.4.30': {} - "@volar/typescript@2.4.30(typescript@5.9.3)": + '@volar/typescript@2.4.30(typescript@5.9.3)': dependencies: - "@volar/language-core": 2.4.30 - "@volar/language-service": 2.4.30(typescript@5.9.3) + '@volar/language-core': 2.4.30 + '@volar/language-service': 2.4.30(typescript@5.9.3) path-browserify: 1.0.1 vscode-uri: 3.2.0 optionalDependencies: typescript: 5.9.3 - "@vscode/emmet-helper@2.11.0": + '@vscode/emmet-helper@2.11.0': dependencies: emmet: 2.4.11 jsonc-parser: 2.3.1 @@ -6638,7 +4629,7 @@ snapshots: vscode-languageserver-types: 3.18.5 vscode-uri: 3.2.0 - "@vscode/l10n@0.0.18": {} + '@vscode/l10n@0.0.18': {} acorn-jsx@5.3.2(acorn@8.19.0): dependencies: @@ -6750,15 +4741,17 @@ snapshots: get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 + assertion-error@2.0.1: {} + astro@5.18.2(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.65.0)(supports-color@7.2.0)(typescript@5.9.3)(yaml@2.9.1): dependencies: - "@astrojs/compiler": 2.13.1 - "@astrojs/internal-helpers": 0.7.6 - "@astrojs/markdown-remark": 6.3.11(supports-color@7.2.0) - "@astrojs/telemetry": 3.3.0(supports-color@7.2.0) - "@capsizecss/unpack": 4.0.1 - "@oslojs/encoding": 1.1.0 - "@rollup/pluginutils": 5.5.1(rollup@4.65.0) + '@astrojs/compiler': 2.13.1 + '@astrojs/internal-helpers': 0.7.6 + '@astrojs/markdown-remark': 6.3.11(supports-color@7.2.0) + '@astrojs/telemetry': 3.3.0(supports-color@7.2.0) + '@capsizecss/unpack': 4.0.1 + '@oslojs/encoding': 1.1.0 + '@rollup/pluginutils': 5.5.1(rollup@4.65.0) acorn: 8.19.0 aria-query: 5.3.2 axobject-query: 4.1.0 @@ -6818,21 +4811,21 @@ snapshots: optionalDependencies: sharp: 0.34.5 transitivePeerDependencies: - - "@azure/app-configuration" - - "@azure/cosmos" - - "@azure/data-tables" - - "@azure/identity" - - "@azure/keyvault-secrets" - - "@azure/storage-blob" - - "@capacitor/preferences" - - "@deno/kv" - - "@netlify/blobs" - - "@planetscale/database" - - "@types/node" - - "@upstash/redis" - - "@vercel/blob" - - "@vercel/functions" - - "@vercel/kv" + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@types/node' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' - aws4fetch - db0 - idb-keyval @@ -6900,6 +4893,8 @@ snapshots: node-releases: 2.0.60 update-browserslist-db: 1.3.4(browserslist@4.29.4) + cac@6.7.14: {} + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -6925,6 +4920,14 @@ snapshots: ccount@2.0.1: {} + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -6938,6 +4941,8 @@ snapshots: character-entities@2.0.2: {} + check-error@2.1.3: {} + chokidar@5.0.0: dependencies: readdirp: 5.1.1 @@ -7040,6 +5045,8 @@ snapshots: dependencies: character-entities: 2.0.2 + deep-eql@5.0.2: {} + deep-is@0.1.4: {} define-data-property@1.1.4: @@ -7110,8 +5117,8 @@ snapshots: emmet@2.4.11: dependencies: - "@emmetio/abbreviation": 2.3.3 - "@emmetio/css-abbreviation": 2.1.8 + '@emmetio/abbreviation': 2.3.3 + '@emmetio/css-abbreviation': 2.1.8 emoji-regex@10.6.0: {} @@ -7241,61 +5248,61 @@ snapshots: esbuild@0.25.12: optionalDependencies: - "@esbuild/aix-ppc64": 0.25.12 - "@esbuild/android-arm": 0.25.12 - "@esbuild/android-arm64": 0.25.12 - "@esbuild/android-x64": 0.25.12 - "@esbuild/darwin-arm64": 0.25.12 - "@esbuild/darwin-x64": 0.25.12 - "@esbuild/freebsd-arm64": 0.25.12 - "@esbuild/freebsd-x64": 0.25.12 - "@esbuild/linux-arm": 0.25.12 - "@esbuild/linux-arm64": 0.25.12 - "@esbuild/linux-ia32": 0.25.12 - "@esbuild/linux-loong64": 0.25.12 - "@esbuild/linux-mips64el": 0.25.12 - "@esbuild/linux-ppc64": 0.25.12 - "@esbuild/linux-riscv64": 0.25.12 - "@esbuild/linux-s390x": 0.25.12 - "@esbuild/linux-x64": 0.25.12 - "@esbuild/netbsd-arm64": 0.25.12 - "@esbuild/netbsd-x64": 0.25.12 - "@esbuild/openbsd-arm64": 0.25.12 - "@esbuild/openbsd-x64": 0.25.12 - "@esbuild/openharmony-arm64": 0.25.12 - "@esbuild/sunos-x64": 0.25.12 - "@esbuild/win32-arm64": 0.25.12 - "@esbuild/win32-ia32": 0.25.12 - "@esbuild/win32-x64": 0.25.12 + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 esbuild@0.27.7: optionalDependencies: - "@esbuild/aix-ppc64": 0.27.7 - "@esbuild/android-arm": 0.27.7 - "@esbuild/android-arm64": 0.27.7 - "@esbuild/android-x64": 0.27.7 - "@esbuild/darwin-arm64": 0.27.7 - "@esbuild/darwin-x64": 0.27.7 - "@esbuild/freebsd-arm64": 0.27.7 - "@esbuild/freebsd-x64": 0.27.7 - "@esbuild/linux-arm": 0.27.7 - "@esbuild/linux-arm64": 0.27.7 - "@esbuild/linux-ia32": 0.27.7 - "@esbuild/linux-loong64": 0.27.7 - "@esbuild/linux-mips64el": 0.27.7 - "@esbuild/linux-ppc64": 0.27.7 - "@esbuild/linux-riscv64": 0.27.7 - "@esbuild/linux-s390x": 0.27.7 - "@esbuild/linux-x64": 0.27.7 - "@esbuild/netbsd-arm64": 0.27.7 - "@esbuild/netbsd-x64": 0.27.7 - "@esbuild/openbsd-arm64": 0.27.7 - "@esbuild/openbsd-x64": 0.27.7 - "@esbuild/openharmony-arm64": 0.27.7 - "@esbuild/sunos-x64": 0.27.7 - "@esbuild/win32-arm64": 0.27.7 - "@esbuild/win32-ia32": 0.27.7 - "@esbuild/win32-x64": 0.27.7 + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 escalade@3.2.0: {} @@ -7342,18 +5349,18 @@ snapshots: eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0): dependencies: - "@eslint-community/eslint-utils": 4.10.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) - "@eslint-community/regexpp": 4.12.2 - "@eslint/config-array": 0.21.2(supports-color@7.2.0) - "@eslint/config-helpers": 0.4.2 - "@eslint/core": 0.17.0 - "@eslint/eslintrc": 3.3.7(supports-color@7.2.0) - "@eslint/js": 9.39.5 - "@eslint/plugin-kit": 0.4.1 - "@humanfs/node": 0.16.8 - "@humanwhocodes/module-importer": 1.0.1 - "@humanwhocodes/retry": 0.4.3 - "@types/estree": 1.0.9 + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2(supports-color@7.2.0) + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.7(supports-color@7.2.0) + '@eslint/js': 9.39.5 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 ajv: 6.15.0 chalk: 4.1.2 cross-spawn: 7.0.6 @@ -7401,12 +5408,14 @@ snapshots: estree-walker@3.0.3: dependencies: - "@types/estree": 1.0.9 + '@types/estree': 1.0.9 esutils@2.0.3: {} eventemitter3@5.0.4: {} + expect-type@1.4.0: {} + extend@3.0.2: {} fast-deep-equal@3.1.3: {} @@ -7451,6 +5460,9 @@ snapshots: dependencies: is-callable: 1.2.7 + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true @@ -7557,7 +5569,7 @@ snapshots: hast-util-from-html@2.0.3: dependencies: - "@types/hast": 3.0.5 + '@types/hast': 3.0.5 devlop: 1.1.0 hast-util-from-parse5: 8.0.3 parse5: 7.3.0 @@ -7566,8 +5578,8 @@ snapshots: hast-util-from-parse5@8.0.3: dependencies: - "@types/hast": 3.0.5 - "@types/unist": 3.0.3 + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 devlop: 1.1.0 hastscript: 9.0.1 property-information: 7.2.0 @@ -7577,17 +5589,17 @@ snapshots: hast-util-is-element@3.0.0: dependencies: - "@types/hast": 3.0.5 + '@types/hast': 3.0.5 hast-util-parse-selector@4.0.0: dependencies: - "@types/hast": 3.0.5 + '@types/hast': 3.0.5 hast-util-raw@9.1.0: dependencies: - "@types/hast": 3.0.5 - "@types/unist": 3.0.3 - "@ungap/structured-clone": 1.4.0 + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + '@ungap/structured-clone': 1.4.0 hast-util-from-parse5: 8.0.3 hast-util-to-parse5: 8.0.1 html-void-elements: 3.0.0 @@ -7601,8 +5613,8 @@ snapshots: hast-util-to-html@9.0.5: dependencies: - "@types/hast": 3.0.5 - "@types/unist": 3.0.3 + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 ccount: 2.0.1 comma-separated-tokens: 2.0.3 hast-util-whitespace: 3.0.0 @@ -7615,7 +5627,7 @@ snapshots: hast-util-to-parse5@8.0.1: dependencies: - "@types/hast": 3.0.5 + '@types/hast': 3.0.5 comma-separated-tokens: 2.0.3 devlop: 1.1.0 property-information: 7.2.0 @@ -7625,18 +5637,18 @@ snapshots: hast-util-to-text@4.0.2: dependencies: - "@types/hast": 3.0.5 - "@types/unist": 3.0.3 + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 hast-util-is-element: 3.0.0 unist-util-find-after: 5.0.0 hast-util-whitespace@3.0.0: dependencies: - "@types/hast": 3.0.5 + '@types/hast': 3.0.5 hastscript@9.0.1: dependencies: - "@types/hast": 3.0.5 + '@types/hast': 3.0.5 comma-separated-tokens: 2.0.3 hast-util-parse-selector: 4.0.0 property-information: 7.2.0 @@ -7810,6 +5822,8 @@ snapshots: js-tokens@4.0.0: {} + js-tokens@9.0.1: {} + js-yaml@4.3.2: dependencies: argparse: 2.0.1 @@ -7911,6 +5925,8 @@ snapshots: dependencies: js-tokens: 4.0.0 + loupe@3.2.1: {} + lru-cache@11.5.3: {} lru-cache@5.1.1: @@ -7919,12 +5935,12 @@ snapshots: magic-string@0.30.21: dependencies: - "@jridgewell/sourcemap-codec": 1.6.0 + '@jridgewell/sourcemap-codec': 1.6.0 magicast@0.5.5: dependencies: - "@babel/parser": 7.29.9 - "@babel/types": 7.29.8 + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 source-map-js: 1.2.2 markdown-table@3.0.4: {} @@ -7933,21 +5949,21 @@ snapshots: mdast-util-definitions@6.0.0: dependencies: - "@types/mdast": 4.0.4 - "@types/unist": 3.0.3 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 unist-util-visit: 5.1.0 mdast-util-find-and-replace@3.0.3: dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 escape-string-regexp: 5.0.0 unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 mdast-util-from-markdown@2.1.0(supports-color@7.2.0): dependencies: - "@types/mdast": 4.0.4 - "@types/unist": 3.0.3 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 decode-named-character-reference: 1.3.0 devlop: 1.1.0 mdast-util-to-string: 4.0.0 @@ -7963,7 +5979,7 @@ snapshots: mdast-util-gfm-autolink-literal@2.0.1: dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 ccount: 2.0.1 devlop: 1.1.0 mdast-util-find-and-replace: 3.0.3 @@ -7971,7 +5987,7 @@ snapshots: mdast-util-gfm-footnote@2.1.0(supports-color@7.2.0): dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.1.0(supports-color@7.2.0) mdast-util-to-markdown: 2.2.0 @@ -7981,7 +5997,7 @@ snapshots: mdast-util-gfm-strikethrough@2.0.1(supports-color@7.2.0): dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 mdast-util-from-markdown: 2.1.0(supports-color@7.2.0) mdast-util-to-markdown: 2.2.0 transitivePeerDependencies: @@ -7989,7 +6005,7 @@ snapshots: mdast-util-gfm-table@2.0.0(supports-color@7.2.0): dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 devlop: 1.1.0 markdown-table: 3.0.4 mdast-util-from-markdown: 2.1.0(supports-color@7.2.0) @@ -7999,7 +6015,7 @@ snapshots: mdast-util-gfm-task-list-item@2.0.0(supports-color@7.2.0): dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.1.0(supports-color@7.2.0) mdast-util-to-markdown: 2.2.0 @@ -8020,14 +6036,14 @@ snapshots: mdast-util-phrasing@4.1.0: dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 unist-util-is: 6.0.1 mdast-util-to-hast@13.2.1: dependencies: - "@types/hast": 3.0.5 - "@types/mdast": 4.0.4 - "@ungap/structured-clone": 1.4.0 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.4.0 devlop: 1.1.0 micromark-util-sanitize-uri: 2.0.1 trim-lines: 3.0.1 @@ -8037,8 +6053,8 @@ snapshots: mdast-util-to-markdown@2.2.0: dependencies: - "@types/mdast": 4.0.4 - "@types/unist": 3.0.3 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 longest-streak: 3.1.0 mdast-util-phrasing: 4.1.0 mdast-util-to-string: 4.0.0 @@ -8051,7 +6067,7 @@ snapshots: mdast-util-to-string@4.0.0: dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 mdn-data@2.0.28: {} @@ -8233,7 +6249,7 @@ snapshots: micromark@4.0.3(supports-color@7.2.0): dependencies: - "@types/debug": 4.1.13 + '@types/debug': 4.1.13 debug: 4.4.3(supports-color@7.2.0) decode-named-character-reference: 1.3.0 devlop: 1.1.0 @@ -8276,7 +6292,7 @@ snapshots: nlcst-to-string@4.0.0: dependencies: - "@types/nlcst": 2.0.3 + '@types/nlcst': 2.0.3 node-exports-info@1.6.2: dependencies: @@ -8392,8 +6408,8 @@ snapshots: parse-latin@7.0.0: dependencies: - "@types/nlcst": 2.0.3 - "@types/unist": 3.0.3 + '@types/nlcst': 2.0.3 + '@types/unist': 3.0.3 nlcst-to-string: 4.0.0 unist-util-modify-children: 4.0.0 unist-util-visit-children: 3.0.0 @@ -8411,6 +6427,10 @@ snapshots: path-parse@1.0.7: {} + pathe@2.0.3: {} + + pathval@2.0.1: {} + piccolore@0.1.3: {} picocolors@1.1.1: {} @@ -8419,6 +6439,14 @@ snapshots: picomatch@4.0.7: {} + playwright-core@1.56.1: {} + + playwright@1.56.1: + dependencies: + playwright-core: 1.56.1 + optionalDependencies: + fsevents: 2.3.2 + possible-typed-array-names@1.1.0: {} postcss@8.5.31: @@ -8502,32 +6530,32 @@ snapshots: rehype-parse@9.0.1: dependencies: - "@types/hast": 3.0.5 + '@types/hast': 3.0.5 hast-util-from-html: 2.0.3 unified: 11.0.5 rehype-raw@7.0.0: dependencies: - "@types/hast": 3.0.5 + '@types/hast': 3.0.5 hast-util-raw: 9.1.0 vfile: 6.0.3 rehype-stringify@10.0.1: dependencies: - "@types/hast": 3.0.5 + '@types/hast': 3.0.5 hast-util-to-html: 9.0.5 unified: 11.0.5 rehype@13.0.2: dependencies: - "@types/hast": 3.0.5 + '@types/hast': 3.0.5 rehype-parse: 9.0.1 rehype-stringify: 10.0.1 unified: 11.0.5 remark-gfm@4.0.1(supports-color@7.2.0): dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 mdast-util-gfm: 3.1.0(supports-color@7.2.0) micromark-extension-gfm: 3.0.0 remark-parse: 11.0.0(supports-color@7.2.0) @@ -8538,7 +6566,7 @@ snapshots: remark-parse@11.0.0(supports-color@7.2.0): dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 mdast-util-from-markdown: 2.1.0(supports-color@7.2.0) micromark-util-types: 2.0.3 unified: 11.0.5 @@ -8547,8 +6575,8 @@ snapshots: remark-rehype@11.1.2: dependencies: - "@types/hast": 3.0.5 - "@types/mdast": 4.0.4 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 mdast-util-to-hast: 13.2.1 unified: 11.0.5 vfile: 6.0.3 @@ -8562,7 +6590,7 @@ snapshots: remark-stringify@11.0.0: dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 mdast-util-to-markdown: 2.2.0 unified: 11.0.5 @@ -8585,59 +6613,59 @@ snapshots: retext-latin@4.0.0: dependencies: - "@types/nlcst": 2.0.3 + '@types/nlcst': 2.0.3 parse-latin: 7.0.0 unified: 11.0.5 retext-smartypants@6.2.0: dependencies: - "@types/nlcst": 2.0.3 + '@types/nlcst': 2.0.3 nlcst-to-string: 4.0.0 unist-util-visit: 5.1.0 retext-stringify@4.0.0: dependencies: - "@types/nlcst": 2.0.3 + '@types/nlcst': 2.0.3 nlcst-to-string: 4.0.0 unified: 11.0.5 retext@9.0.0: dependencies: - "@types/nlcst": 2.0.3 + '@types/nlcst': 2.0.3 retext-latin: 4.0.0 retext-stringify: 4.0.0 unified: 11.0.5 rollup@4.65.0: dependencies: - "@types/estree": 1.0.9 + '@types/estree': 1.0.9 optionalDependencies: - "@napi-rs/lzma-linux-x64-gnu": 1.5.1 - "@rollup/rollup-android-arm-eabi": 4.65.0 - "@rollup/rollup-android-arm64": 4.65.0 - "@rollup/rollup-darwin-arm64": 4.65.0 - "@rollup/rollup-darwin-x64": 4.65.0 - "@rollup/rollup-freebsd-arm64": 4.65.0 - "@rollup/rollup-freebsd-x64": 4.65.0 - "@rollup/rollup-linux-arm-gnueabihf": 4.65.0 - "@rollup/rollup-linux-arm-musleabihf": 4.65.0 - "@rollup/rollup-linux-arm64-gnu": 4.65.0 - "@rollup/rollup-linux-arm64-musl": 4.65.0 - "@rollup/rollup-linux-loong64-gnu": 4.65.0 - "@rollup/rollup-linux-loong64-musl": 4.65.0 - "@rollup/rollup-linux-ppc64-gnu": 4.65.0 - "@rollup/rollup-linux-ppc64-musl": 4.65.0 - "@rollup/rollup-linux-riscv64-gnu": 4.65.0 - "@rollup/rollup-linux-riscv64-musl": 4.65.0 - "@rollup/rollup-linux-s390x-gnu": 4.65.0 - "@rollup/rollup-linux-x64-gnu": 4.65.0 - "@rollup/rollup-linux-x64-musl": 4.65.0 - "@rollup/rollup-openbsd-x64": 4.65.0 - "@rollup/rollup-openharmony-arm64": 4.65.0 - "@rollup/rollup-win32-arm64-msvc": 4.65.0 - "@rollup/rollup-win32-ia32-msvc": 4.65.0 - "@rollup/rollup-win32-x64-gnu": 4.65.0 - "@rollup/rollup-win32-x64-msvc": 4.65.0 + '@napi-rs/lzma-linux-x64-gnu': 1.5.1 + '@rollup/rollup-android-arm-eabi': 4.65.0 + '@rollup/rollup-android-arm64': 4.65.0 + '@rollup/rollup-darwin-arm64': 4.65.0 + '@rollup/rollup-darwin-x64': 4.65.0 + '@rollup/rollup-freebsd-arm64': 4.65.0 + '@rollup/rollup-freebsd-x64': 4.65.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.65.0 + '@rollup/rollup-linux-arm-musleabihf': 4.65.0 + '@rollup/rollup-linux-arm64-gnu': 4.65.0 + '@rollup/rollup-linux-arm64-musl': 4.65.0 + '@rollup/rollup-linux-loong64-gnu': 4.65.0 + '@rollup/rollup-linux-loong64-musl': 4.65.0 + '@rollup/rollup-linux-ppc64-gnu': 4.65.0 + '@rollup/rollup-linux-ppc64-musl': 4.65.0 + '@rollup/rollup-linux-riscv64-gnu': 4.65.0 + '@rollup/rollup-linux-riscv64-musl': 4.65.0 + '@rollup/rollup-linux-s390x-gnu': 4.65.0 + '@rollup/rollup-linux-x64-gnu': 4.65.0 + '@rollup/rollup-linux-x64-musl': 4.65.0 + '@rollup/rollup-openbsd-x64': 4.65.0 + '@rollup/rollup-openharmony-arm64': 4.65.0 + '@rollup/rollup-win32-arm64-msvc': 4.65.0 + '@rollup/rollup-win32-ia32-msvc': 4.65.0 + '@rollup/rollup-win32-x64-gnu': 4.65.0 + '@rollup/rollup-win32-x64-msvc': 4.65.0 fsevents: 2.3.3 safe-array-concat@1.1.4: @@ -8693,34 +6721,34 @@ snapshots: sharp@0.34.5: dependencies: - "@img/colour": 1.1.0 + '@img/colour': 1.1.0 detect-libc: 2.1.2 semver: 7.8.5 optionalDependencies: - "@img/sharp-darwin-arm64": 0.34.5 - "@img/sharp-darwin-x64": 0.34.5 - "@img/sharp-libvips-darwin-arm64": 1.2.4 - "@img/sharp-libvips-darwin-x64": 1.2.4 - "@img/sharp-libvips-linux-arm": 1.2.4 - "@img/sharp-libvips-linux-arm64": 1.2.4 - "@img/sharp-libvips-linux-ppc64": 1.2.4 - "@img/sharp-libvips-linux-riscv64": 1.2.4 - "@img/sharp-libvips-linux-s390x": 1.2.4 - "@img/sharp-libvips-linux-x64": 1.2.4 - "@img/sharp-libvips-linuxmusl-arm64": 1.2.4 - "@img/sharp-libvips-linuxmusl-x64": 1.2.4 - "@img/sharp-linux-arm": 0.34.5 - "@img/sharp-linux-arm64": 0.34.5 - "@img/sharp-linux-ppc64": 0.34.5 - "@img/sharp-linux-riscv64": 0.34.5 - "@img/sharp-linux-s390x": 0.34.5 - "@img/sharp-linux-x64": 0.34.5 - "@img/sharp-linuxmusl-arm64": 0.34.5 - "@img/sharp-linuxmusl-x64": 0.34.5 - "@img/sharp-wasm32": 0.34.5 - "@img/sharp-win32-arm64": 0.34.5 - "@img/sharp-win32-ia32": 0.34.5 - "@img/sharp-win32-x64": 0.34.5 + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 optional: true shebang-command@2.0.0: @@ -8731,14 +6759,14 @@ snapshots: shiki@3.23.0: dependencies: - "@shikijs/core": 3.23.0 - "@shikijs/engine-javascript": 3.23.0 - "@shikijs/engine-oniguruma": 3.23.0 - "@shikijs/langs": 3.23.0 - "@shikijs/themes": 3.23.0 - "@shikijs/types": 3.23.0 - "@shikijs/vscode-textmate": 10.1.0 - "@types/hast": 3.0.5 + '@shikijs/core': 3.23.0 + '@shikijs/engine-javascript': 3.23.0 + '@shikijs/engine-oniguruma': 3.23.0 + '@shikijs/langs': 3.23.0 + '@shikijs/themes': 3.23.0 + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.1.0 + '@types/hast': 3.0.5 side-channel-list@1.0.1: dependencies: @@ -8768,6 +6796,8 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + siginfo@2.0.0: {} + sisteransi@1.0.5: {} smol-toml@1.9.1: {} @@ -8776,6 +6806,10 @@ snapshots: space-separated-tokens@2.0.2: {} + stackback@0.0.2: {} + + std-env@3.10.0: {} + stop-iteration-iterator@1.1.0: dependencies: es-errors: 1.3.0 @@ -8858,6 +6892,10 @@ snapshots: strip-json-comments@3.1.1: {} + strip-literal@3.1.0: + dependencies: + js-tokens: 9.0.1 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -8882,6 +6920,10 @@ snapshots: tiny-inflate@1.0.3: {} + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + tinyexec@1.3.1: {} tinyglobby@0.2.17: @@ -8889,6 +6931,12 @@ snapshots: fdir: 6.5.0(picomatch@4.0.7) picomatch: 4.0.7 + tinypool@1.1.1: {} + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.6: {} + trim-lines@3.0.1: {} trough@2.2.0: {} @@ -8966,7 +7014,7 @@ snapshots: unified@11.0.5: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 bail: 2.0.2 devlop: 1.1.0 extend: 3.0.2 @@ -8982,43 +7030,43 @@ snapshots: unist-util-find-after@5.0.0: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 unist-util-is: 6.0.1 unist-util-is@6.0.1: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 unist-util-modify-children@4.0.0: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 array-iterate: 2.0.1 unist-util-position@5.0.0: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 unist-util-remove-position@5.0.0: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 unist-util-visit: 5.1.0 unist-util-stringify-position@4.0.0: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 unist-util-visit-children@3.0.0: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 unist-util-visit-parents@6.0.2: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 unist-util-is: 6.0.1 unist-util-visit@5.1.0: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 @@ -9045,19 +7093,40 @@ snapshots: vfile-location@5.0.3: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 vfile: 6.0.3 vfile-message@4.0.3: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 unist-util-stringify-position: 4.0.0 vfile@6.0.3: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 vfile-message: 4.0.3 + vite-node@3.2.4(jiti@2.7.0)(lightningcss@1.32.0)(supports-color@7.2.0)(yaml@2.9.1): + dependencies: + cac: 6.7.14 + debug: 4.4.3(supports-color@7.2.0) + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 6.4.5(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.1) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vite@6.4.5(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.1): dependencies: esbuild: 0.25.12 @@ -9076,22 +7145,63 @@ snapshots: optionalDependencies: vite: 6.4.5(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.1) + vitest@3.2.4(@types/debug@4.1.13)(jiti@2.7.0)(lightningcss@1.32.0)(supports-color@7.2.0)(yaml@2.9.1): + dependencies: + '@types/chai': 5.2.3 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@6.4.5(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.1)) + '@vitest/pretty-format': 3.2.7 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.3(supports-color@7.2.0) + expect-type: 1.4.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.7 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.17 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 6.4.5(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.1) + vite-node: 3.2.4(jiti@2.7.0)(lightningcss@1.32.0)(supports-color@7.2.0)(yaml@2.9.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.13 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + volar-service-css@0.0.71(@volar/language-service@2.4.30(typescript@5.9.3)): dependencies: vscode-css-languageservice: 6.3.10 vscode-languageserver-textdocument: 1.0.16 vscode-uri: 3.2.0 optionalDependencies: - "@volar/language-service": 2.4.30(typescript@5.9.3) + '@volar/language-service': 2.4.30(typescript@5.9.3) volar-service-emmet@0.0.71(@volar/language-service@2.4.30(typescript@5.9.3)): dependencies: - "@emmetio/css-parser": 0.4.1 - "@emmetio/html-matcher": 1.3.0 - "@vscode/emmet-helper": 2.11.0 + '@emmetio/css-parser': 0.4.1 + '@emmetio/html-matcher': 1.3.0 + '@vscode/emmet-helper': 2.11.0 vscode-uri: 3.2.0 optionalDependencies: - "@volar/language-service": 2.4.30(typescript@5.9.3) + '@volar/language-service': 2.4.30(typescript@5.9.3) volar-service-html@0.0.71(@volar/language-service@2.4.30(typescript@5.9.3)): dependencies: @@ -9099,20 +7209,20 @@ snapshots: vscode-languageserver-textdocument: 1.0.16 vscode-uri: 3.2.0 optionalDependencies: - "@volar/language-service": 2.4.30(typescript@5.9.3) + '@volar/language-service': 2.4.30(typescript@5.9.3) volar-service-prettier@0.0.71(@volar/language-service@2.4.30(typescript@5.9.3))(prettier@3.9.10): dependencies: vscode-uri: 3.2.0 optionalDependencies: - "@volar/language-service": 2.4.30(typescript@5.9.3) + '@volar/language-service': 2.4.30(typescript@5.9.3) prettier: 3.9.10 volar-service-typescript-twoslash-queries@0.0.71(@volar/language-service@2.4.30(typescript@5.9.3))(typescript@5.9.3): dependencies: vscode-uri: 3.2.0 optionalDependencies: - "@volar/language-service": 2.4.30(typescript@5.9.3) + '@volar/language-service': 2.4.30(typescript@5.9.3) typescript: 5.9.3 volar-service-typescript@0.0.71(@volar/language-service@2.4.30(typescript@5.9.3))(typescript@5.9.3): @@ -9124,7 +7234,7 @@ snapshots: vscode-nls: 5.2.0 vscode-uri: 3.2.0 optionalDependencies: - "@volar/language-service": 2.4.30(typescript@5.9.3) + '@volar/language-service': 2.4.30(typescript@5.9.3) typescript: 5.9.3 volar-service-yaml@0.0.71(@volar/language-service@2.4.30(typescript@5.9.3)): @@ -9132,18 +7242,18 @@ snapshots: vscode-uri: 3.2.0 yaml-language-server: 1.23.0 optionalDependencies: - "@volar/language-service": 2.4.30(typescript@5.9.3) + '@volar/language-service': 2.4.30(typescript@5.9.3) vscode-css-languageservice@6.3.10: dependencies: - "@vscode/l10n": 0.0.18 + '@vscode/l10n': 0.0.18 vscode-languageserver-textdocument: 1.0.16 vscode-languageserver-types: 3.17.5 vscode-uri: 3.2.0 vscode-html-languageservice@5.6.2: dependencies: - "@vscode/l10n": 0.0.18 + '@vscode/l10n': 0.0.18 vscode-languageserver-textdocument: 1.0.16 vscode-languageserver-types: 3.18.5 vscode-uri: 3.2.0 @@ -9233,6 +7343,11 @@ snapshots: dependencies: isexe: 2.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + widest-line@5.0.0: dependencies: string-width: 7.2.0 @@ -9253,7 +7368,7 @@ snapshots: yaml-language-server@1.23.0: dependencies: - "@vscode/l10n": 0.0.18 + '@vscode/l10n': 0.0.18 ajv: 8.20.0 ajv-draft-04: 1.0.0(ajv@8.20.0) ajv-i18n: 4.2.0(ajv@8.20.0) diff --git a/web/public/tiny_cpu.mjs b/web/public/tiny_cpu.mjs new file mode 100644 index 0000000..efff4ce --- /dev/null +++ b/web/public/tiny_cpu.mjs @@ -0,0 +1,2 @@ +async function createTinyCpu(moduleArg={}){var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";if(ENVIRONMENT_IS_NODE){const{createRequire}=await(import("node:module"));var require=createRequire(import.meta.url)}var thisProgram="./this.program";var _scriptName=import.meta.url;var scriptDirectory="";function locateFile(path){if(Module["locateFile"])return Module["locateFile"](path,scriptDirectory);return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("node:fs");if(_scriptName.startsWith("file:"))scriptDirectory=require("node:path").dirname(require("node:url").fileURLToPath(_scriptName))+"/";readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?void 0:"utf8");return ret};if(process.argv.length>1)thisProgram=process.argv[1].replace(/\\/g,"/");process.argv.slice(2);0}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}if(ENVIRONMENT_IS_WORKER)readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)};readAsync=async url=>{if(isFileURI(url))return new Promise((resolve,reject)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){resolve(xhr.response);return}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)});var response=await fetch(url,{credentials:"same-origin"});if(response.ok)return response.arrayBuffer();throw new Error(response.status+" : "+response.url)}}var out=console.log.bind(console);var err=console.error.bind(console);var ABORT=false;var isFileURI=filename=>filename.startsWith("file://");function getMemoryBuffer(){return wasmMemory.buffer}function updateMemoryViews(){if(HEAP8?.buffer?.resizable)return;var b=getMemoryBuffer();HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);HEAPU8=new Uint8Array(b);HEAPU16=new Uint16Array(b);HEAP32=new Int32Array(b);HEAPU32=new Uint32Array(b);HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){var preRun=Module["preRun"];if(preRun){if(typeof preRun==="function")preRun=[preRun];onPreRuns.push(...preRun)}callRuntimeCallbacks(onPreRuns)}function initRuntime(){0;if(!Module["noFSInit"]&&!FS.initialized)FS.init();TTY.init();wasmExports["__wasm_call_ctors"]();FS.ignorePermissions=false}function postRun(){var postRun=Module["postRun"];if(postRun){if(typeof postRun==="function")postRun=[postRun];onPostRuns.push(...postRun)}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what=`Aborted(${what})`;err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";throw new WebAssembly.RuntimeError(what)}var wasmBinaryFile;function findWasmBinary(){if(Module["locateFile"])return locateFile("tiny_cpu.wasm");return new URL("tiny_cpu.wasm",import.meta.url).href}function getBinarySync(file){if(readBinary)return readBinary(file);throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(true)try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE)try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={env:wasmImports,wasi_snapshot_preview1:wasmImports};return imports}var HEAP8;var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0)callbacks.shift()(Module)};var onPostRuns=[];var onPreRuns=[];var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var UTF8Decoder=globalThis.TextDecoder&&new TextDecoder;var findStringEnd=(heapOrArray,idx,maxBytesToRead,ignoreNul)=>{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder)return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr));var str="";while(idx>10,56320|ch&1023)}}return str};var HEAPU8;var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead,ignoreNul):"";var HEAPU32;class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}}var uncaughtExceptionCount=0;var __Unwind_RaiseException=()=>{abort()};var HEAP32;var syscallGetVarargI=()=>{var ret=HEAP32[+SYSCALLS.varargs>>2];SYSCALLS.varargs+=4;return ret};var syscallGetVarargP=syscallGetVarargI;var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(filename).slice(1),normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last===".")parts.splice(i,1);else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot)for(;up;up--)parts.unshift("..");return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.slice(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute)path=".";if(path&&trailingSlash)path+="/";return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir)return".";if(dir)dir=dir.slice(0,-1);return root+dir},basename:path=>path&&path.match(/([^\/]+|\/)\/*$/)[1],join:(...paths)=>PATH.normalize(paths.join("/")),join2:(l,r)=>PATH.normalize(l+"/"+r)};var initRandomFill=()=>{if(ENVIRONMENT_IS_NODE){var nodeCrypto=require("node:crypto");return view=>(nodeCrypto.randomFillSync(view),0)}return view=>(crypto.getRandomValues(view),0)};var randomFill=view=>(randomFill=initRandomFill())(view);var PATH_FS={resolve:(...args)=>{var resolvedPath="",resolvedAbsolute=false;for(var i=args.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?args[i]:FS.cwd();if(typeof path!=="string")throw new TypeError("Arguments to path.resolve must be strings");else if(!path)return"";resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).slice(1);to=PATH_FS.resolve(to).slice(1);function trim(arr){var start=0;for(;start=0;end--)if(arr[end]!=="")break;if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else len+=3}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var intArrayFromString=(stringy,dontAddNull,length)=>{var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array};var FS_stdin_getChar=()=>{if(!FS_stdin_getChar_buffer.length){var result=null;if(ENVIRONMENT_IS_NODE){var buf=Buffer.alloc(256);var bytesRead=0;try{bytesRead=fs.readSync(process.stdin.fd,buf,0,256)}catch(e){if(e.toString().includes("EOF"))bytesRead=0;else throw e}if(bytesRead>0)result=buf.toString("utf-8",0,bytesRead)}else if(globalThis.window?.prompt){result=window.prompt("Input: ");if(result!==null)result+="\n"}if(!result)return null;FS_stdin_getChar_buffer=intArrayFromString(result,true)}return FS_stdin_getChar_buffer.shift()};var TTY={ttys:[],init(){},shutdown(){},register(dev,ops){TTY.ttys[dev]={input:[],output:[],ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty)throw new FS.ErrnoError(43);stream.tty=tty;stream.seekable=false},close(stream){stream.tty.ops.fsync(stream.tty)},fsync(stream){stream.tty.ops.fsync(stream.tty)},read(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char)throw new FS.ErrnoError(60);var bytesRead=0;for(var i=0;i0){out(UTF8ArrayToString(tty.output));tty.output=[]}},ioctl_tcgets(tty){return{c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(tty,optional_actions,data){return 0},ioctl_tiocgwinsz(tty){return[24,80]}},default_tty1_ops:{put_char(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output));tty.output=[]}else if(val!=0)tty.output.push(val)},fsync(tty){if(tty.output?.length>0){err(UTF8ArrayToString(tty.output));tty.output=[]}}}};var mmapAlloc=()=>{abort()};var MEMFS={ops_table:null,mount(mount){return MEMFS.createNode(null,"/",16895,0)},createNode(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode))throw new FS.ErrnoError(63);MEMFS.ops_table||={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}};var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=MEMFS.emptyFileContents??=new Uint8Array(0)}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.atime=node.mtime=node.ctime=Date.now();if(parent){parent.contents[name]=node;parent.atime=parent.mtime=parent.ctime=node.atime}return node},getFileDataAsTypedArray(node){return node.contents.subarray(0,node.usedBytes)},expandFileStorage(node,newCapacity){var prevCapacity=node.contents.length;if(prevCapacity>=newCapacity)return;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity<1048576?2:1.125)>>>0);if(prevCapacity)newCapacity=Math.max(newCapacity,256);var oldContents=MEMFS.getFileDataAsTypedArray(node);node.contents=new Uint8Array(newCapacity);node.contents.set(oldContents)},resizeFileStorage(node,newSize){if(node.usedBytes==newSize)return;var oldContents=node.contents;node.contents=new Uint8Array(newSize);node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)));node.usedBytes=newSize},node_ops:{getattr(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode))attr.size=4096;else if(FS.isFile(node.mode))attr.size=node.usedBytes;else if(FS.isLink(node.mode))attr.size=node.link.length;else attr.size=0;attr.atime=new Date(node.atime);attr.mtime=new Date(node.mtime);attr.ctime=new Date(node.ctime);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr(node,attr){for(const key of["mode","atime","mtime","ctime"])if(attr[key]!=null)node[key]=attr[key];if(attr.size!==void 0)MEMFS.resizeFileStorage(node,attr.size)},lookup(parent,name){if(!MEMFS.doesNotExistError){MEMFS.doesNotExistError=new FS.ErrnoError(44);MEMFS.doesNotExistError.stack=""}throw MEMFS.doesNotExistError},mknod(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename(old_node,new_dir,new_name){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node&&FS.isDir(old_node.mode))for(var i in new_node.contents)throw new FS.ErrnoError(55);delete old_node.parent.contents[old_node.name];new_dir.contents[new_name]=old_node;old_node.name=new_name;new_dir.ctime=new_dir.mtime=old_node.parent.ctime=old_node.parent.mtime=Date.now()},unlink(parent,name){delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},rmdir(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents)throw new FS.ErrnoError(55);delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},readdir(node){return[".","..",...Object.keys(node.contents)]},symlink(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,41471,0);node.link=oldpath;return node},readlink(node){if(!FS.isLink(node.mode))throw new FS.ErrnoError(28);return node.link}},stream_ops:{read(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);buffer.set(contents.subarray(position,position+size),offset);return size},write(stream,buffer,offset,length,position,canOwn){if(buffer.buffer===HEAP8.buffer)canOwn=false;if(!length)return 0;var node=stream.node;node.mtime=node.ctime=Date.now();if(canOwn){node.contents=buffer.subarray(offset,offset+length);node.usedBytes=length}else if(!node.usedBytes&&!position){node.contents=buffer.slice(offset,offset+length);node.usedBytes=length}else{MEMFS.expandFileStorage(node,position+length);node.contents.set(buffer.subarray(offset,offset+length),position);node.usedBytes=Math.max(node.usedBytes,position+length)}return length},llseek(stream,offset,whence){var position=offset;if(whence===1)position+=stream.position;else if(whence===2)if(FS.isFile(stream.node.mode))position+=stream.node.usedBytes;if(position<0)throw new FS.ErrnoError(28);return position},mmap(stream,length,position,prot,flags){if(!FS.isFile(stream.node.mode))throw new FS.ErrnoError(43);var ptr;var allocated;var contents=stream.node.contents;if(!(flags&2)&&contents.buffer===HEAP8.buffer){allocated=false;ptr=contents.byteOffset}else{allocated=true;ptr=mmapAlloc();if(!ptr)throw new FS.ErrnoError(48);if(contents){if(position>0||position+length{if(typeof str!=="string")return str;var flags={r:0,"r+":2,w:577,"w+":578,a:1089,"a+":1090}[str];if(typeof flags==="undefined")throw new Error(`Unknown file open mode: ${str}`);return flags};var FS_fileDataToTypedArray=data=>{if(typeof data==="string")data=intArrayFromString(data,true);if(!data.subarray)data=new Uint8Array(data);return data};var FS_getMode=(canRead,canWrite)=>{var mode=0;if(canRead)mode|=365;if(canWrite)mode|=146;return mode};var asyncLoad=async url=>{var arrayBuffer=await readAsync(url);return new Uint8Array(arrayBuffer)};var FS_createDataFile=(...args)=>FS.createDataFile(...args);var getUniqueRunDependency=id=>id;var dependenciesPromise=null;var resolveRunDependencies=async()=>dependenciesPromise;var runDependencies=0;var dependenciesPromiseResolve=null;var removeRunDependency=()=>{runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(!runDependencies)dependenciesPromiseResolve()};var addRunDependency=()=>{if(!runDependencies)dependenciesPromise=new Promise(resolve=>dependenciesPromiseResolve=resolve);runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)};var preloadPlugins=[];var FS_handledByPreloadPlugin=async(byteArray,fullname)=>{if(typeof Browser!=="undefined")Browser.init();for(var plugin of preloadPlugins)if(plugin["canHandle"](fullname))return plugin["handle"](byteArray,fullname);return byteArray};var FS_preloadFile=async(parent,name,url,canRead,canWrite,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;getUniqueRunDependency(`cp ${fullname}`);addRunDependency();try{var byteArray=url;if(typeof url==="string")byteArray=await asyncLoad(url);byteArray=await FS_handledByPreloadPlugin(byteArray,fullname);preFinish?.();if(!dontCreateFile)FS_createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}finally{removeRunDependency()}};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:[],currentPath:"/",initialized:false,ignorePermissions:true,syncFSRequests:0,ErrnoError:class{name="ErrnoError";constructor(errno){this.errno=errno}},FSStream:class{shared={};get object(){return this.node}set object(val){this.node=val}get isRead(){return(this.flags&2097155)!==1}get isWrite(){return(this.flags&2097155)!==0}get isAppend(){return this.flags&1024}get flags(){return this.shared.flags}set flags(val){this.shared.flags=val}get position(){return this.shared.position}set position(val){this.shared.position=val}},FSNode:class{node_ops={};stream_ops={};readMode=365;writeMode=146;mounted=null;constructor(parent,name,mode,rdev){if(!parent)parent=this;this.parent=parent;this.mount=parent.mount;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.rdev=rdev;this.atime=this.mtime=this.ctime=Date.now()}get read(){return(this.mode&this.readMode)===this.readMode}set read(val){val?this.mode|=this.readMode:this.mode&=~this.readMode}get write(){return(this.mode&this.writeMode)===this.writeMode}set write(val){val?this.mode|=this.writeMode:this.mode&=~this.writeMode}get isFolder(){return FS.isDir(this.mode)}get isDevice(){return FS.isChrdev(this.mode)}addListener(cb,exclusive=false){var entry={cb,exclusive};var listeners=this.listeners??=new Set;listeners.add(entry);return{listeners,entry}}notifyListeners(flags){if(!this.listeners)return;var excl;for(var entry of this.listeners)if(entry.exclusive)(excl||=[]).push(entry);else entry.cb(flags);if(excl){var i=(this.exclTurn||0)%excl.length;this.exclTurn=i+1;excl[i].cb(flags)}}},lookupPath(path,opts={}){if(!path)throw new FS.ErrnoError(44);opts.follow_mount??=true;if(!PATH.isAbs(path))path=FS.cwd()+"/"+path;linkloop:for(var nlinks=0;nlinks<40;nlinks++){var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i>>0)%FS.nameTable.length},hashAddNode(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node)FS.nameTable[hash]=node.name_next;else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode(parent,name){var errCode=FS.mayLookup(parent);if(errCode)throw new FS.ErrnoError(errCode);var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name)return node}return FS.lookup(parent,name)},createNode(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode(node){FS.hashRemoveNode(node)},isRoot(node){return node===node.parent},isMountpoint(node){return!!node.mounted},isFile(mode){return(mode&61440)===32768},isDir(mode){return(mode&61440)===16384},isLink(mode){return(mode&61440)===40960},isChrdev(mode){return(mode&61440)===8192},isBlkdev(mode){return(mode&61440)===24576},isFIFO(mode){return(mode&61440)===4096},isSocket(mode){return(mode&49152)===49152},flagsToPermissionString(flag){var perms=["r","w","rw"][flag&3];if(flag&512)perms+="w";return perms},nodePermissions(node,perms){if(FS.ignorePermissions)return 0;if(perms.includes("r")&&!(node.mode&292))return 2;if(perms.includes("w")&&!(node.mode&146))return 2;if(perms.includes("x")&&!(node.mode&73))return 2;return 0},mayLookup(dir){if(!FS.isDir(dir.mode))return 54;var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate(dir,name){if(!FS.isDir(dir.mode))return 54;try{FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode)return errCode;if(isdir){if(!FS.isDir(node.mode))return 54;if(FS.isRoot(node)||FS.getPath(node)===FS.cwd())return 10}else if(FS.isDir(node.mode))return 31;return 0},mayOpen(node,flags){if(!node)return 44;if(FS.isLink(node.mode))return 32;var mode=FS.flagsToPermissionString(flags);if(FS.isDir(node.mode))if(mode!=="r"||flags&576)return 31;return FS.nodePermissions(node,mode)},checkOpExists(op,err){if(!op)throw new FS.ErrnoError(err);return op},MAX_OPEN_FDS:4096,nextfd(){for(var fd=0;fd<=FS.MAX_OPEN_FDS;fd++)if(!FS.streams[fd])return fd;throw new FS.ErrnoError(33)},getStreamChecked(fd){var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);return stream},getStream:fd=>FS.streams[fd],createStream(stream,fd=-1){stream=Object.assign(new FS.FSStream,stream);if(fd==-1)fd=FS.nextfd();stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream(fd){FS.streams[fd]=null},dupStream(origStream,fd=-1){var stream=FS.createStream(origStream,fd);stream.stream_ops?.dup?.(stream);return stream},doSetAttr(stream,node,attr){var setattr=stream?.stream_ops.setattr;var arg=setattr?stream:node;setattr??=node.node_ops.setattr;FS.checkOpExists(setattr,63);try{setattr(arg,attr)}catch(e){if(e instanceof RangeError)throw new FS.ErrnoError(22);throw e}},chrdev_stream_ops:{open(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;stream.stream_ops.open?.(stream)},llseek(){throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push(...m.mounts)}return mounts},syncfs(populate,callback){if(typeof populate==="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1)err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`);var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length)doCallback(null)}for(var mount of mounts)if(mount.type.syncfs)mount.type.syncfs(mount,populate,done);else done(null)},mount(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root)throw new FS.ErrnoError(10);else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node))throw new FS.ErrnoError(10);if(!FS.isDir(node.mode))throw new FS.ErrnoError(54)}var mount={type,opts,mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root)FS.root=mountRoot;else if(node){node.mounted=mount;if(node.mount)node.mount.mounts.push(mount)}return mountRoot},unmount(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node))throw new FS.ErrnoError(28);var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);for(var[hash,current]of Object.entries(FS.nameTable))while(current){var next=current.name_next;if(mounts.includes(current.mount))FS.destroyNode(current);current=next}node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup(parent,name){return parent.node_ops.lookup(parent,name)},mknod(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name)throw new FS.ErrnoError(28);if(name==="."||name==="..")throw new FS.ErrnoError(20);var errCode=FS.mayCreate(parent,name);if(errCode)throw new FS.ErrnoError(errCode);if(!parent.node_ops.mknod)throw new FS.ErrnoError(63);return parent.node_ops.mknod(parent,name,mode,dev)},statfs(path){return FS.statfsNode(FS.lookupPath(path,{follow:true}).node)},statfsStream(stream){return FS.statfsNode(stream.node)},statfsNode(node){var rtn={bsize:4096,frsize:4096,blocks:1e6,bfree:5e5,bavail:5e5,files:FS.nextInode,ffree:FS.nextInode-1,fsid:42,flags:2,namelen:255};if(node.node_ops.statfs)Object.assign(rtn,node.node_ops.statfs(node.mount.opts.root));return rtn},create(path,mode=438){mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir(path,mode=511){mode&=1023;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree(path,mode=511){var dirs=path.split("/");var d="";for(var dir of dirs){if(!dir)continue;if(d||PATH.isAbs(path))d+="/";d+=dir;try{FS.mkdir(d,mode)}catch(e){if(e.errno!=20)throw e}}},mkdev(path,mode,dev){if(typeof dev==="undefined"){dev=mode;mode=438}mode|=8192;return FS.mknod(path,mode,dev)},symlink(oldpath,newpath){if(!PATH_FS.resolve(oldpath))throw new FS.ErrnoError(44);var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent)throw new FS.ErrnoError(44);var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode)throw new FS.ErrnoError(errCode);if(!parent.node_ops.symlink)throw new FS.ErrnoError(63);return parent.node_ops.symlink(parent,newname,oldpath)},link(oldpath,newpath,flags){var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent)throw new FS.ErrnoError(44);var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode)throw new FS.ErrnoError(errCode);if(!parent.node_ops.link)throw new FS.ErrnoError(34);return parent.node_ops.link(parent,newname,oldpath,flags)},rename(old_path,new_path){var old_dirname=PATH.dirname(old_path);var new_dirname=PATH.dirname(new_path);var old_name=PATH.basename(old_path);var new_name=PATH.basename(new_path);var lookup,old_dir,new_dir;lookup=FS.lookupPath(old_path,{parent:true});old_dir=lookup.node;lookup=FS.lookupPath(new_path,{parent:true});new_dir=lookup.node;if(!old_dir||!new_dir)throw new FS.ErrnoError(44);if(old_dir.mount!==new_dir.mount)throw new FS.ErrnoError(75);var old_node=FS.lookupNode(old_dir,old_name);var relative=PATH_FS.relative(old_path,new_dirname);if(relative.charAt(0)!==".")throw new FS.ErrnoError(28);relative=PATH_FS.relative(new_path,old_dirname);if(relative.charAt(0)!==".")throw new FS.ErrnoError(55);var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(old_node===new_node)return;var isdir=FS.isDir(old_node.mode);var errCode=FS.mayDelete(old_dir,old_name,isdir);if(errCode)throw new FS.ErrnoError(errCode);errCode=new_node?FS.mayDelete(new_dir,new_name,isdir):FS.mayCreate(new_dir,new_name);if(errCode)throw new FS.ErrnoError(errCode);if(!old_dir.node_ops.rename)throw new FS.ErrnoError(63);if(FS.isMountpoint(old_node)||new_node&&FS.isMountpoint(new_node))throw new FS.ErrnoError(10);if(new_dir!==old_dir){errCode=FS.nodePermissions(old_dir,"w");if(errCode)throw new FS.ErrnoError(errCode)}FS.hashRemoveNode(old_node);try{old_dir.node_ops.rename(old_node,new_dir,new_name);if(new_node)FS.hashRemoveNode(new_node);old_node.parent=new_dir}catch(e){throw e}finally{FS.hashAddNode(old_node)}},rmdir(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,true);if(errCode)throw new FS.ErrnoError(errCode);if(!parent.node_ops.rmdir)throw new FS.ErrnoError(63);if(FS.isMountpoint(node))throw new FS.ErrnoError(10);parent.node_ops.rmdir(parent,name);FS.destroyNode(node)},readdir(path){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;var readdir=FS.checkOpExists(node.node_ops.readdir,54);return readdir(node)},unlink(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;if(!parent)throw new FS.ErrnoError(44);var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,false);if(errCode)throw new FS.ErrnoError(errCode);if(!parent.node_ops.unlink)throw new FS.ErrnoError(63);if(FS.isMountpoint(node))throw new FS.ErrnoError(10);parent.node_ops.unlink(parent,name);FS.destroyNode(node)},readlink(path){var lookup=FS.lookupPath(path);var link=lookup.node;if(!link)throw new FS.ErrnoError(44);if(!link.node_ops.readlink)throw new FS.ErrnoError(28);return link.node_ops.readlink(link)},stat(path,dontFollow=false){var lookup=FS.lookupPath(path,{follow:!dontFollow});var node=lookup.node;var getattr=FS.checkOpExists(node.node_ops.getattr,63);return getattr(node)},fstat(fd){var stream=FS.getStreamChecked(fd);var node=stream.node;var getattr=stream.stream_ops.getattr;var arg=getattr?stream:node;getattr??=node.node_ops.getattr;FS.checkOpExists(getattr,63);return getattr(arg)},lstat(path){return FS.stat(path,true)},doChmod(stream,node,mode,dontFollow=false){FS.doSetAttr(stream,node,{mode:mode&4095|node.mode&-4096,ctime:Date.now(),dontFollow})},chmod(path,mode,dontFollow=false){var node;if(typeof path==="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else node=path;FS.doChmod(null,node,mode,dontFollow)},lchmod(path,mode){FS.chmod(path,mode,true)},fchmod(fd,mode){var stream=FS.getStreamChecked(fd);FS.doChmod(stream,stream.node,mode,false)},doChown(stream,node,dontFollow=false){FS.doSetAttr(stream,node,{timestamp:Date.now(),dontFollow})},chown(path,uid,gid,dontFollow=false){var node;if(typeof path==="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else node=path;FS.doChown(null,node,dontFollow)},lchown(path,uid,gid){FS.chown(path,uid,gid,true)},fchown(fd,uid,gid){var stream=FS.getStreamChecked(fd);FS.doChown(stream,stream.node,false)},doTruncate(stream,node,len){if(FS.isDir(node.mode))throw new FS.ErrnoError(31);if(!FS.isFile(node.mode))throw new FS.ErrnoError(28);var errCode=FS.nodePermissions(node,"w");if(errCode)throw new FS.ErrnoError(errCode);FS.doSetAttr(stream,node,{size:len,timestamp:Date.now()})},truncate(path,len){if(len<0)throw new FS.ErrnoError(28);var node;if(typeof path==="string"){var lookup=FS.lookupPath(path,{follow:true});node=lookup.node}else node=path;FS.doTruncate(null,node,len)},ftruncate(fd,len){var stream=FS.getStreamChecked(fd);if(len<0||(stream.flags&2097155)===0)throw new FS.ErrnoError(28);FS.doTruncate(stream,stream.node,len)},utime(path,atime,mtime,dontFollow=false){var lookup=FS.lookupPath(path,{follow:!dontFollow});FS.doSetAttr(null,lookup.node,{atime,mtime,dontFollow})},open(path,flags,mode=438){if(path==="")throw new FS.ErrnoError(44);flags=FS_modeStringToFlags(flags);if(flags&64)mode=mode&4095|32768;else mode=0;var node;var isDirPath;if(typeof path==="object")node=path;else{isDirPath=path.endsWith("/");var lookup=FS.lookupPath(path,{follow:!(flags&131072),noent_okay:true});node=lookup.node;path=lookup.path}var created=false;if(flags&64)if(node){if(flags&128)throw new FS.ErrnoError(20)}else if(isDirPath)throw new FS.ErrnoError(31);else{node=FS.mknod(path,mode|511,0);created=true}if(!node)throw new FS.ErrnoError(44);if(FS.isChrdev(node.mode))flags&=-513;if(flags&65536&&!FS.isDir(node.mode))throw new FS.ErrnoError(54);if(!created){var errCode=FS.mayOpen(node,flags);if(errCode)throw new FS.ErrnoError(errCode)}if(flags&512&&!created)FS.truncate(node,0);flags&=-131713;var stream=FS.createStream({node,path:FS.getPath(node),flags,seekable:true,position:0,stream_ops:node.stream_ops,ungotten:[],error:false});if(stream.stream_ops.open)stream.stream_ops.open(stream);if(created)FS.chmod(node,mode&511);return stream},close(stream){if(FS.isClosed(stream))throw new FS.ErrnoError(8);if(stream.getdents)stream.getdents=null;stream.node?.notifyListeners(32);try{if(stream.stream_ops.close)stream.stream_ops.close(stream)}catch(e){throw e}finally{FS.closeStream(stream.fd)}stream.fd=null},isClosed(stream){return stream.fd===null},llseek(stream,offset,whence){if(FS.isClosed(stream))throw new FS.ErrnoError(8);if(!stream.seekable||!stream.stream_ops.llseek)throw new FS.ErrnoError(70);if(whence!=0&&whence!=1&&whence!=2)throw new FS.ErrnoError(28);stream.position=stream.stream_ops.llseek(stream,offset,whence);stream.ungotten=[];return stream.position},read(stream,buffer,offset,length,position){if(length<0||position<0)throw new FS.ErrnoError(28);if(FS.isClosed(stream))throw new FS.ErrnoError(8);if((stream.flags&2097155)===1)throw new FS.ErrnoError(8);if(FS.isDir(stream.node.mode))throw new FS.ErrnoError(31);if(!stream.stream_ops.read)throw new FS.ErrnoError(28);var seeking=typeof position!=="undefined";if(!seeking)position=stream.position;else if(!stream.seekable)throw new FS.ErrnoError(70);var bytesRead=stream.stream_ops.read(stream,buffer,offset,length,position);if(!seeking)stream.position+=bytesRead;return bytesRead},write(stream,buffer,offset,length,position,canOwn){if(length<0||position<0)throw new FS.ErrnoError(28);if(FS.isClosed(stream))throw new FS.ErrnoError(8);if((stream.flags&2097155)===0)throw new FS.ErrnoError(8);if(FS.isDir(stream.node.mode))throw new FS.ErrnoError(31);if(!stream.stream_ops.write)throw new FS.ErrnoError(28);if(stream.seekable&&stream.flags&1024)FS.llseek(stream,0,2);var seeking=typeof position!=="undefined";if(!seeking)position=stream.position;else if(!stream.seekable)throw new FS.ErrnoError(70);var bytesWritten=stream.stream_ops.write(stream,buffer,offset,length,position,canOwn);if(!seeking)stream.position+=bytesWritten;return bytesWritten},mmap(stream,length,position,prot,flags){if(prot&2&&!(flags&2)&&(stream.flags&2097155)!==2)throw new FS.ErrnoError(2);if((stream.flags&2097155)===1)throw new FS.ErrnoError(2);if(!stream.stream_ops.mmap)throw new FS.ErrnoError(43);if(!length)throw new FS.ErrnoError(28);return stream.stream_ops.mmap(stream,length,position,prot,flags)},msync(stream,buffer,offset,length,mmapFlags){if(!stream.stream_ops.msync)return 0;return stream.stream_ops.msync(stream,buffer,offset,length,mmapFlags)},ioctl(stream,cmd,arg){if(!stream.stream_ops.ioctl)throw new FS.ErrnoError(59);return stream.stream_ops.ioctl(stream,cmd,arg)},readFile(path,opts={}){opts.flags??=0;opts.encoding??="binary";if(opts.encoding!=="utf8"&&opts.encoding!=="binary")abort(`Invalid encoding type "${opts.encoding}"`);var stream=FS.open(path,opts.flags);var stat=FS.stat(path);var length=stat.size;var buf=new Uint8Array(length);FS.read(stream,buf,0,length,0);if(opts.encoding==="utf8")buf=UTF8ArrayToString(buf);FS.close(stream);return buf},writeFile(path,data,opts={}){opts.flags??=577;var stream=FS.open(path,opts.flags,opts.mode);data=FS_fileDataToTypedArray(data);FS.write(stream,data,0,data.byteLength,void 0,opts.canOwn);FS.close(stream)},cwd:()=>FS.currentPath,chdir(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null)throw new FS.ErrnoError(44);if(!FS.isDir(lookup.node.mode))throw new FS.ErrnoError(54);var errCode=FS.nodePermissions(lookup.node,"x");if(errCode)throw new FS.ErrnoError(errCode);FS.currentPath=lookup.path},createDefaultDirectories(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length)=>length,llseek:()=>0});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(!randomLeft){randomFill(randomBuffer);randomLeft=randomBuffer.byteLength}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories(){FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount(){var node=FS.createNode(proc_self,"fd",16895,73);node.stream_ops={llseek:MEMFS.stream_ops.llseek};node.node_ops={lookup(parent,name){var fd=+name;var stream=FS.getStreamChecked(fd);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path},id:fd+1};ret.parent=ret;return ret},readdir(){return Array.from(FS.streams.entries()).filter(([k,v])=>v).map(([k,v])=>k.toString())}};return node}},{},"/proc/self/fd")},createStandardStreams(input,output,error){if(input)FS.createDevice("/dev","stdin",input);else FS.symlink("/dev/tty","/dev/stdin");if(output)FS.createDevice("/dev","stdout",null,output);else FS.symlink("/dev/tty","/dev/stdout");if(error)FS.createDevice("/dev","stderr",null,error);else FS.symlink("/dev/tty1","/dev/stderr");FS.open("/dev/stdin",0);FS.open("/dev/stdout",1);FS.open("/dev/stderr",1)},staticInit(){FS.nameTable.length=4096;FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories()},init(input,output,error){FS.initialized=true;input??=Module["stdin"];output??=Module["stdout"];error??=Module["stderr"];FS.createStandardStreams(input,output,error)},quit(){FS.initialized=false;for(var stream of FS.streams)if(stream)FS.close(stream)},analyzePath(path,dontResolveLastLink=false){try{var lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});path=lookup.path}catch(e){}var ret={isRoot:false,exists:false,error:0,name:null,path:null,object:null,parentExists:false,parentPath:null,parentObject:null};try{lookup=FS.lookupPath(path,{parent:true});ret.parentExists=true;ret.parentPath=lookup.path;ret.parentObject=lookup.node;ret.name=PATH.basename(path);lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});ret.exists=true;ret.path=lookup.path;ret.object=lookup.node;ret.name=lookup.node.name;ret.isRoot=lookup.path==="/"}catch(e){ret.error=e.errno}return ret},createPath(parent,path,canRead,canWrite){parent=typeof parent==="string"?parent:FS.getPath(parent);var parts=path.split("/").reverse();while(parts.length){var part=parts.pop();if(!part)continue;var current=PATH.join2(parent,part);try{FS.mkdir(current)}catch(e){if(e.errno!=20)throw e}parent=current}return current},createFile(parent,name,properties,canRead,canWrite){var path=PATH.join2(typeof parent==="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(canRead,canWrite);return FS.create(path,mode)},createDataFile(parent,name,data,canRead,canWrite,canOwn){var path=name;if(parent){parent=typeof parent==="string"?parent:FS.getPath(parent);path=name?PATH.join2(parent,name):parent}var mode=FS_getMode(canRead,canWrite);var node=FS.create(path,mode);if(data){data=FS_fileDataToTypedArray(data);FS.chmod(node,mode|146);var stream=FS.open(node,577);FS.write(stream,data,0,data.length,0,canOwn);FS.close(stream);FS.chmod(node,mode)}},createDevice(parent,name,input,output){var path=PATH.join2(typeof parent==="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(!!input,!!output);FS.createDevice.major??=64;var dev=FS.makedev(FS.createDevice.major++,0);FS.registerDevice(dev,{open(stream){stream.seekable=false},close(stream){if(output?.buffer?.length)output(10)},read(stream,buffer,offset,length,pos){var bytesRead=0;for(var i=0;ithis.length-1||idx<0)return;var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]}setDataGetter(getter){this.getter=getter}cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))abort(`Couldn't load ${url}. Status: ${xhr.status}`);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1048576;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)abort(`invalid range (${from}, ${to}) or no bytes requested!`);if(to>datalength-1)abort(`only ${datalength} bytes available! programmer error!`);var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range",`bytes=${from}-${to}`);xhr.responseType="arraybuffer";if(xhr.overrideMimeType)xhr.overrideMimeType("text/plain; charset=x-user-defined");xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))abort(`Couldn't load ${url}. Status: ${xhr.status}`);if(xhr.response!==void 0)return new Uint8Array(xhr.response||[]);return intArrayFromString(xhr.responseText??"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]==="undefined")lazyArray.chunks[chunkNum]=doXHR(start,end);if(typeof lazyArray.chunks[chunkNum]==="undefined")abort("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true}get length(){if(!this.lengthKnown)this.cacheLength();return this._length}get chunkSize(){if(!this.lengthKnown)this.cacheLength();return this._chunkSize}}if(globalThis.XMLHttpRequest){if(!ENVIRONMENT_IS_WORKER)abort("Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc");var lazyArray=new LazyUint8Array;var properties={isDevice:false,contents:lazyArray}}else properties={isDevice:false,url};var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents)node.contents=properties.contents;else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};for(const[key,fn]of Object.entries(node.stream_ops))stream_ops[key]=(...args)=>{FS.forceLoadFile(node);return fn(...args)};function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice)for(var i=0;i{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position)=>{FS.forceLoadFile(node);var ptr=mmapAlloc();if(!ptr)throw new FS.ErrnoError(48);writeChunks(stream,HEAP8,ptr,length,position);return{ptr,allocated:true}};node.stream_ops=stream_ops;return node}};var HEAP64;var SYSCALLS={currentUmask:18,calculateAt(dirfd,path,allowEmpty=false){if(PATH.isAbs(path))return path;var dir;if(dirfd===-100)dir=FS.cwd();else{var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path}if(path.length==0){if(!allowEmpty)throw new FS.ErrnoError(44);return dir}return dir+"/"+path},writeStat(buf,stat){HEAPU32[buf>>2]=stat.dev;HEAPU32[buf+4>>2]=stat.mode;HEAPU32[buf+8>>2]=stat.nlink;HEAPU32[buf+12>>2]=stat.uid;HEAPU32[buf+16>>2]=stat.gid;HEAPU32[buf+20>>2]=stat.rdev;HEAP64[buf+24>>3]=BigInt(stat.size);HEAP32[buf+32>>2]=4096;HEAP32[buf+36>>2]=stat.blocks;var atime=stat.atime.getTime();var mtime=stat.mtime.getTime();var ctime=stat.ctime.getTime();HEAP64[buf+40>>3]=BigInt(Math.floor(atime/1e3));HEAPU32[buf+48>>2]=atime%1e3*1e3*1e3;HEAP64[buf+56>>3]=BigInt(Math.floor(mtime/1e3));HEAPU32[buf+64>>2]=mtime%1e3*1e3*1e3;HEAP64[buf+72>>3]=BigInt(Math.floor(ctime/1e3));HEAPU32[buf+80>>2]=ctime%1e3*1e3*1e3;HEAP64[buf+88>>3]=BigInt(stat.ino);return 0},writeStatFs(buf,stats){HEAPU32[buf+4>>2]=stats.bsize;HEAPU32[buf+60>>2]=stats.bsize;HEAP64[buf+8>>3]=BigInt(stats.blocks);HEAP64[buf+16>>3]=BigInt(stats.bfree);HEAP64[buf+24>>3]=BigInt(stats.bavail);HEAP64[buf+32>>3]=BigInt(stats.files);HEAP64[buf+40>>3]=BigInt(stats.ffree);HEAPU32[buf+48>>2]=stats.fsid;HEAPU32[buf+64>>2]=stats.flags;HEAPU32[buf+56>>2]=stats.namelen},doMsync(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode))throw new FS.ErrnoError(43);if(flags&2)return 0;var buffer=HEAPU8.subarray(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},getStreamFromFD(fd){var stream=FS.getStreamChecked(fd);return stream},varargs:void 0,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};var HEAP16;var AsciiToString=ptr=>{var str="";while(1){var ch=HEAPU8[ptr++];if(!ch)return str;str+=String.fromCharCode(ch)}};var awaitingDependencies={};var registeredTypes={};var typeDependencies={};class BindingError extends Error{constructor(message){super(message);this.name="BindingError"}}var throwBindingError=message=>{throw new BindingError(message)};function sharedRegisterType(rawType,registeredInstance,options={}){var name=registeredInstance.name;if(!rawType)throwBindingError(`type "${name}" must have a positive integer typeid pointer`);if(registeredTypes.hasOwnProperty(rawType))if(options.ignoreDuplicateRegistrations)return;else throwBindingError(`Cannot register type '${name}' twice`);registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(cb=>cb())}}function registerType(rawType,registeredInstance,options={}){return sharedRegisterType(rawType,registeredInstance,options)}var HEAPU16;var HEAPU64;var integerReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?pointer=>HEAP8[pointer]:pointer=>HEAPU8[pointer];case 2:return signed?pointer=>HEAP16[pointer>>1]:pointer=>HEAPU16[pointer>>1];case 4:return signed?pointer=>HEAP32[pointer>>2]:pointer=>HEAPU32[pointer>>2];case 8:return signed?pointer=>HEAP64[pointer>>3]:pointer=>HEAPU64[pointer>>3];default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};var shallowCopyInternalPointer=o=>({count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType});var throwInstanceAlreadyDeleted=obj=>{throwBindingError(function(handle){return handle.$$.ptrType.registeredClass.name}(obj)+" instance already deleted")};var finalizationRegistry=false;var detachFinalizer=()=>{};var runDestructor=$$=>{if($$.smartPtr)$$.smartPtrType.rawDestructor($$.smartPtr);else $$.ptrType.registeredClass.rawDestructor($$.ptr)};var releaseClassHandle=$$=>{$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete)runDestructor($$)};var attachFinalizer=handle=>{if(!globalThis.FinalizationRegistry){attachFinalizer=handle=>handle;return handle}finalizationRegistry=new FinalizationRegistry(info=>{releaseClassHandle(info.$$)});attachFinalizer=handle=>{var $$=handle.$$;var hasSmartPtr=!!$$.smartPtr;if(hasSmartPtr){var info={$$};finalizationRegistry.register(handle,info,handle)}return handle};detachFinalizer=handle=>finalizationRegistry.unregister(handle);return attachFinalizer(handle)};var deletionQueue=[];var flushPendingDeletes=()=>{while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}};function ClassHandle(){}var createNamedFunction=(name,func)=>Object.defineProperty(func,"name",{value:name});var registeredPointers={};var ensureOverloadTable=(proto,methodName,humanName)=>{if(void 0===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(...args){if(!proto[methodName].overloadTable.hasOwnProperty(args.length))throwBindingError(`Function '${humanName}' called with an invalid number of arguments (${args.length}) - expects one of (${proto[methodName].overloadTable})!`);return proto[methodName].overloadTable[args.length].apply(this,args)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}};var exposePublicSymbol=(name,value,numArguments)=>{if(Module.hasOwnProperty(name)){if(void 0===numArguments||void 0!==Module[name].overloadTable&&void 0!==Module[name].overloadTable[numArguments])throwBindingError(`Cannot register public name '${name}' twice`);ensureOverloadTable(Module,name,name);if(Module[name].overloadTable.hasOwnProperty(numArguments))throwBindingError(`Cannot register multiple overloads of a function with the same number of arguments (${numArguments})!`);Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}};var makeLegalFunctionName=name=>{name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=48&&f<=57)return`_${name}`;return name};function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}var upcastPointer=(ptr,ptrClass,desiredClass)=>{while(ptrClass!==desiredClass){if(!ptrClass.upcast)throwBindingError(`Expected null or instance of ${desiredClass.name}, got an instance of ${ptrClass.name}`);ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr};var embindRepr=v=>{if(v===null)return"null";var t=typeof v;if(t==="object"||t==="array"||t==="function")return v.toString();else return""+v};function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference)throwBindingError(`null is not a valid ${this.name}`);return 0}if(!handle.$$)throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`);if(!handle.$$.ptr)throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`);var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference)throwBindingError(`null is not a valid ${this.name}`);if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null)destructors.push(this.rawDestructor,ptr);return ptr}else return 0}if(!handle||!handle.$$)throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`);if(!handle.$$.ptr)throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`);if(!this.isConst&&handle.$$.ptrType.isConst)throwBindingError(`Cannot convert argument of type ${handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name} to parameter type ${this.name}`);var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(void 0===handle.$$.smartPtr)throwBindingError("Passing raw pointer to smart pointer is illegal");switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this)ptr=handle.$$.smartPtr;else throwBindingError(`Cannot convert argument of type ${handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name} to parameter type ${this.name}`);break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this)ptr=handle.$$.smartPtr;else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,Emval.toHandle(()=>clonedHandle["delete"]()));if(destructors!==null)destructors.push(this.rawDestructor,ptr)}break;default:throwBindingError("Unsupported sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference)throwBindingError(`null is not a valid ${this.name}`);return 0}if(!handle.$$)throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`);if(!handle.$$.ptr)throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`);if(handle.$$.ptrType.isConst)throwBindingError(`Cannot convert argument of type ${handle.$$.ptrType.name} to parameter type ${this.name}`);var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function readPointer(pointer){return this.fromWireType(HEAPU32[pointer>>2])}var downcastPointer=(ptr,ptrClass,desiredClass)=>{if(ptrClass===desiredClass)return ptr;if(void 0===desiredClass.baseClass)return null;var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null)return null;return desiredClass.downcast(rv)};var registeredInstances={};var getBasestPointer=(class_,ptr)=>{if(ptr===void 0)throwBindingError("ptr should not be undefined");while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr};var getInheritedInstance=(class_,ptr)=>{ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]};class InternalError extends Error{constructor(message){super(message);this.name="InternalError"}}var throwInternalError=message=>{throw new InternalError(message)};var makeClassHandle=(prototype,record)=>{if(!record.ptrType||!record.ptr)throwInternalError("makeClassHandle requires ptr and ptrType");var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr)throwInternalError("Both smartPtrType and smartPtr must be specified");record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record,writable:true}}))};function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(void 0!==registeredInstance)if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}function makeDefaultHandle(){if(this.isSmartPointer)return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr});else return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr})}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord)return makeDefaultHandle.call(this);var toType;if(this.isConst)toType=registeredPointerRecord.constPointerType;else toType=registeredPointerRecord.pointerType;var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null)return makeDefaultHandle.call(this);if(this.isSmartPointer)return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr});else return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&®isteredClass.baseClass===void 0)if(isConst){this.toWireType=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this.toWireType=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else this.toWireType=genericPointerToWireType}var replacePublicSymbol=(name,value,numArguments)=>{if(!Module.hasOwnProperty(name))throwInternalError("Replacing nonexistent public symbol");if(void 0!==Module[name].overloadTable&&void 0!==numArguments)Module[name].overloadTable[numArguments]=value;else{Module[name]=value;Module[name].argCount=numArguments}};var wasmTableMirror=[];var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func)wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr);return func};var embind__requireFunction=(signature,rawFunction)=>{signature=AsciiToString(signature);var fp=function(){var rtn=getWasmTableEntry(rawFunction);return rtn}();if(typeof fp!=="function")throwBindingError(`unknown function pointer with signature ${signature}: ${rawFunction}`);return fp};class UnboundTypeError extends Error{}var getTypeName=type=>{var ptr=___getTypeName(type);var rv=AsciiToString(ptr);_free(ptr);return rv};var throwUnboundTypeError=(message,types)=>{var unboundTypes=[];var seen={};types.forEach(function visit(type){if(seen[type])return;if(registeredTypes[type])return;if(typeDependencies[type]){typeDependencies[type].forEach(visit);return}unboundTypes.push(type);seen[type]=true});throw new UnboundTypeError(`${message}: `+unboundTypes.map(getTypeName).join([", "]))};var whenDependentTypesAreResolved=(myTypes,dependentTypes,getTypeConverters)=>{myTypes.forEach(type=>typeDependencies[type]=dependentTypes);function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length)throwInternalError("Mismatched type converter count");for(var i=0;i{typeConverters[i]=registeredTypes[dt];++registered;if(registered===unregisteredTypes.length)onComplete(typeConverters)})}if(0===unregisteredTypes.length)onComplete(typeConverters)};var heap32VectorToArray=(count,firstElement)=>{var array=[];for(var i=0;i>2]);return array};var runDestructors=destructors=>{while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}};function usesDestructorStack(argTypes){for(var i=1;i{signature=signature.trim();const argsIndex=signature.indexOf("(");if(argsIndex===-1)return signature;return signature.slice(0,argsIndex)};var emval_freelist=[];var emval_handles=[0,1,,1,null,1,true,1,false,1];var __emval_decref=handle=>{if(handle>9&&0===--emval_handles[handle+1]){emval_handles[handle];emval_handles[handle]=void 0;emval_freelist.push(handle)}};var Emval={toValue:handle=>{if(!handle)throwBindingError(`Cannot use deleted val. handle = ${handle}`);return emval_handles[handle]},toHandle:value=>{switch(value){case void 0:return 2;case null:return 4;case true:return 6;case false:return 8;default:{const handle=emval_freelist.pop()||emval_handles.length;emval_handles[handle]=value;emval_handles[handle+1]=1;return handle}}}};var EmValType={name:"emscripten::val",fromWireType:handle=>{var rv=Emval.toValue(handle);__emval_decref(handle);return rv},toWireType:(destructors,value)=>Emval.toHandle(value),readValueFromPointer:readPointer,destructorFunction:null};var HEAPF32;var HEAPF64;var floatReadValueFromPointer=(name,width)=>{switch(width){case 4:return function(pointer){return this.fromWireType(HEAPF32[pointer>>2])};case 8:return function(pointer){return this.fromWireType(HEAPF64[pointer>>3])};default:throw new TypeError(`invalid float width (${width}): ${name}`)}};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var UTF16Decoder=globalThis.TextDecoder?new TextDecoder("utf-16le"):void 0;var UTF16ToString=(ptr,maxBytesToRead,ignoreNul)=>{var idx=ptr>>1;var endIdx=findStringEnd(HEAPU16,idx,maxBytesToRead/2,ignoreNul);if(endIdx-idx>16&&UTF16Decoder)return UTF16Decoder.decode(HEAPU16.subarray(idx,endIdx));var str="";for(var i=idx;i{if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr};var lengthBytesUTF16=str=>str.length*2;var UTF32ToString=(ptr,maxBytesToRead,ignoreNul)=>{var str="";var startIdx=ptr>>2;for(var i=0;!(i>=maxBytesToRead/4);i++){var utf32=HEAPU32[startIdx+i];if(!utf32&&!ignoreNul)break;str+=String.fromCodePoint(utf32)}return str};var stringToUTF32=(str,outPtr,maxBytesToWrite=2147483647)=>{if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i65535)i++;HEAP32[outPtr>>2]=codePoint;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr};var lengthBytesUTF32=str=>{var len=0;for(var i=0;i65535)i++;len+=4}return len};var emval_methodCallers=[];var emval_addMethodCaller=caller=>{var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id};var requireRegisteredType=(rawType,humanName)=>{var impl=registeredTypes[rawType];if(void 0===impl)throwBindingError(`${humanName} has unknown type ${getTypeName(rawType)}`);return impl};var emval_lookupTypes=(argCount,argTypes)=>{var a=new Array(argCount);for(var i=0;i>2],`parameter ${i}`);return a};var emval_returnValue=(toReturnWire,destructorsRef,handle)=>{var destructors=[];var result=toReturnWire(destructors,handle);if(destructors.length)HEAPU32[destructorsRef>>2]=Emval.toHandle(destructors);return result};var emval_symbols={};var getStringOrSymbol=address=>{var symbol=emval_symbols[address];if(symbol===void 0)return AsciiToString(address);return symbol};var _emscripten_get_now=()=>performance.now();var _emscripten_date_now=()=>Date.now();var checkWasiClock=clock_id=>clock_id>=0&&clock_id<=3;var bigintToI53Checked=num=>num<-9007199254740992||num>9007199254740992?NaN:Number(num);var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var ENV={};var getExecutableName=()=>thisProgram;var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(globalThis.navigator?.language??"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV)if(ENV[x]===void 0)delete env[x];else env[x]=ENV[x];var strings=[];for(var x in env)strings.push(`${x}=${env[x]}`);getEnvStrings.strings=strings}return getEnvStrings.strings};var doReadv=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;try{var curr=FS.read(stream,HEAP8,ptr,len,offset)}catch(e){if(ret>0&&e instanceof FS.ErrnoError&&(e.errno==6||e.errno==6))break;throw e}if(curr<0)return-1;ret+=curr;if(curr{if(iovcnt==1)return FS.write(stream,HEAP8,HEAPU32[iov>>2],HEAPU32[iov+4>>2],offset);var total=0;for(var i=0,p=iov;i>2];var view=new Uint8Array(total);var voff=0;for(i=0;i>2];var len=HEAPU32[iov+4>>2];view.set(HEAPU8.subarray(ptr,ptr+len),voff);voff+=len}return FS.write(stream,view,0,total,offset)};FS.createPreloadedFile=(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{FS_preloadFile(parent,name,url,canRead,canWrite,dontCreateFile,canOwn,preFinish).then(onload).catch(onerror)};FS.preloadFile=FS_preloadFile;FS.staticInit();(()=>{let proto=ClassHandle.prototype;Object.assign(proto,{isAliasOf(other){if(!(this instanceof ClassHandle))return false;if(!(other instanceof ClassHandle))return false;var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;other.$$=other.$$;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right},clone(){if(!this.$$.ptr)throwInstanceAlreadyDeleted(this);if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}},delete(){if(!this.$$.ptr)throwInstanceAlreadyDeleted(this);if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete)throwBindingError("Object already scheduled for deletion");detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=void 0;this.$$.ptr=void 0}},isDeleted(){return!this.$$.ptr},deleteLater(){if(!this.$$.ptr)throwInstanceAlreadyDeleted(this);if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete)throwBindingError("Object already scheduled for deletion");deletionQueue.push(this);if(deletionQueue.length===1&&void 0)(void 0)(flushPendingDeletes);this.$$.deleteScheduled=true;return this}});const symbolDispose=Symbol.dispose;if(symbolDispose)proto[symbolDispose]=proto["delete"]})();(()=>{Object.assign(RegisteredPointer.prototype,{getPointee(ptr){if(this.rawGetPointee)ptr=this.rawGetPointee(ptr);return ptr},destructor(ptr){this.rawDestructor?.(ptr)},readValueFromPointer:readPointer,fromWireType:RegisteredPointer_fromWireType})})();if(Module["noExitRuntime"])Module["noExitRuntime"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["arguments"])Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];var preInit=Module["preInit"];if(preInit){if(typeof preInit==="function")Module["preInit"]=preInit=[preInit];while(preInit.length>0)preInit.shift()()}var _malloc,_free,___getTypeName,__emscripten_stack_restore,_emscripten_stack_get_current,wasmMemory,wasmTable;function assignWasmExports(wasmExports){_malloc=wasmExports["malloc"];_free=wasmExports["free"];___getTypeName=wasmExports["__getTypeName"];wasmExports["setThrew"];__emscripten_stack_restore=wasmExports["_emscripten_stack_restore"];wasmExports["_emscripten_stack_alloc"];_emscripten_stack_get_current=wasmExports["emscripten_stack_get_current"];wasmMemory=wasmExports["memory"];wasmTable=wasmExports["__indirect_function_table"]}var wasmImports={__assert_fail:(condition,filename,line,func)=>abort(`Assertion failed: ${UTF8ToString(condition)}, at: `+[filename?UTF8ToString(filename):"unknown filename",line,func?UTF8ToString(func):"unknown function"]),__cxa_throw:(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);uncaughtExceptionCount++;__Unwind_RaiseException()},__syscall_fcntl64:function(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:var arg=syscallGetVarargI();if(arg<0)return-28;while(FS.streams[arg])arg++;var newStream;newStream=FS.dupStream(stream,arg);return newStream.fd;case 1:case 2:return 0;case 3:return stream.flags;case 4:arg=syscallGetVarargI();stream.flags=stream.flags&-289793|arg&289792;return 0;case 12:arg=syscallGetVarargP();HEAP16[arg+0>>1]=2;return 0;case 13:case 14:return 0}return-28}catch(e){if(typeof FS==="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}},__syscall_ioctl:function(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:if(!stream.tty)return-59;return 0;case 21505:if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcgets){var termios=stream.tty.ops.ioctl_tcgets(stream);var argp=syscallGetVarargP();HEAP32[argp>>2]=termios.c_iflag||0;HEAP32[argp+4>>2]=termios.c_oflag||0;HEAP32[argp+8>>2]=termios.c_cflag||0;HEAP32[argp+12>>2]=termios.c_lflag||0;for(var i=0;i<32;i++)HEAP8[argp+i+17]=termios.c_cc[i]||0;return 0}return 0;case 21510:case 21511:case 21512:if(!stream.tty)return-59;return 0;case 21506:case 21507:case 21508:if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcsets){argp=syscallGetVarargP();var c_iflag=HEAP32[argp>>2];var c_oflag=HEAP32[argp+4>>2];var c_cflag=HEAP32[argp+8>>2];var c_lflag=HEAP32[argp+12>>2];var c_cc=[];for(i=0;i<32;i++)c_cc.push(HEAP8[argp+i+17]);return stream.tty.ops.ioctl_tcsets(stream.tty,op,{c_iflag,c_oflag,c_cflag,c_lflag,c_cc})}return 0;case 21519:if(!stream.tty)return-59;argp=syscallGetVarargP();HEAP32[argp>>2]=0;return 0;case 21520:if(!stream.tty)return-59;return-28;case 21537:case 21531:argp=syscallGetVarargP();return FS.ioctl(stream,op,argp);case 21523:if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tiocgwinsz){var winsize=stream.tty.ops.ioctl_tiocgwinsz(stream.tty);argp=syscallGetVarargP();HEAP16[argp>>1]=winsize[0];HEAP16[argp+2>>1]=winsize[1]}return 0;case 21524:if(!stream.tty)return-59;return 0;case 21515:if(!stream.tty)return-59;return 0;default:return-28}}catch(e){if(typeof FS==="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}},__syscall_openat:function(dirfd,path,flags,varargs){SYSCALLS.varargs=varargs;try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);var mode=varargs?syscallGetVarargI():0;if(flags&64)mode&=~SYSCALLS.currentUmask;return FS.open(path,flags,mode).fd}catch(e){if(typeof FS==="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}},_abort_js:()=>abort(""),_embind_register_bigint:(primitiveType,name,size,minRange,maxRange)=>{name=AsciiToString(name);const isUnsignedType=minRange===0n;let fromWireType=value=>value;if(isUnsignedType){const bitSize=size*8;fromWireType=value=>BigInt.asUintN(bitSize,value);maxRange=fromWireType(maxRange)}registerType(primitiveType,{name,fromWireType,toWireType:(destructors,value)=>{if(typeof value==="number")value=BigInt(value);return value},readValueFromPointer:integerReadValueFromPointer(name,size,!isUnsignedType),destructorFunction:null})},_embind_register_bool:(rawType,name,trueValue,falseValue)=>{name=AsciiToString(name);registerType(rawType,{name,fromWireType:function(wt){return!!wt},toWireType:function(destructors,o){return o?trueValue:falseValue},readValueFromPointer:function(pointer){return this.fromWireType(HEAPU8[pointer])},destructorFunction:null})},_embind_register_class:(rawType,rawPointerType,rawConstPointerType,baseClassRawType,getActualTypeSignature,getActualType,upcastSignature,upcast,downcastSignature,downcast,name,destructorSignature,rawDestructor)=>{name=AsciiToString(name);getActualType=embind__requireFunction(getActualTypeSignature,getActualType);upcast&&=embind__requireFunction(upcastSignature,upcast);downcast&&=embind__requireFunction(downcastSignature,downcast);rawDestructor=embind__requireFunction(destructorSignature,rawDestructor);var legalFunctionName=makeLegalFunctionName(name);exposePublicSymbol(legalFunctionName,function(){throwUnboundTypeError(`Cannot construct ${name} due to unbound types`,[baseClassRawType])});whenDependentTypesAreResolved([rawType,rawPointerType,rawConstPointerType],baseClassRawType?[baseClassRawType]:[],base=>{base=base[0];var baseClass;var basePrototype;if(baseClassRawType){baseClass=base.registeredClass;basePrototype=baseClass.instancePrototype}else basePrototype=ClassHandle.prototype;var constructor=createNamedFunction(name,function(...args){if(Object.getPrototypeOf(this)!==instancePrototype)throw new BindingError(`Use 'new' to construct ${name}`);if(void 0===registeredClass.constructor_body)throw new BindingError(`${name} has no accessible constructor`);var body=registeredClass.constructor_body[args.length];if(void 0===body)throw new BindingError(`Tried to invoke ctor of ${name} with invalid number of parameters (${args.length}) - expected (${Object.keys(registeredClass.constructor_body).toString()}) parameters instead!`);return body.apply(this,args)});var instancePrototype=Object.create(basePrototype,{constructor:{value:constructor}});constructor.prototype=instancePrototype;var registeredClass=new RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast);if(registeredClass.baseClass){registeredClass.baseClass.__derivedClasses??=[];registeredClass.baseClass.__derivedClasses.push(registeredClass)}var referenceConverter=new RegisteredPointer(name,registeredClass,true,false,false);var pointerConverter=new RegisteredPointer(name+"*",registeredClass,false,false,false);var constPointerConverter=new RegisteredPointer(name+" const*",registeredClass,false,true,false);registeredPointers[rawType]={pointerType:pointerConverter,constPointerType:constPointerConverter};replacePublicSymbol(legalFunctionName,constructor);return[referenceConverter,pointerConverter,constPointerConverter]})},_embind_register_class_constructor:(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`constructor ${classType.name}`;if(void 0===classType.registeredClass.constructor_body)classType.registeredClass.constructor_body=[];if(void 0!==classType.registeredClass.constructor_body[argCount-1])throw new BindingError(`Cannot register multiple constructors with identical number of parameters (${argCount-1}) for class '${classType.name}'! Overload resolution is currently only performed using the parameter count, not actual type info!`);classType.registeredClass.constructor_body[argCount-1]=()=>{throwUnboundTypeError(`Cannot construct ${classType.name} due to unbound types`,rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{argTypes.splice(1,0,null);classType.registeredClass.constructor_body[argCount-1]=craftInvokerFunction(humanName,argTypes,null,invoker,rawConstructor);return[]});return[]})},_embind_register_class_function:(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,context,isPureVirtual,isAsync)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=AsciiToString(methodName);methodName=getFunctionName(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`${classType.name}.${methodName}`;if(methodName.startsWith("@@"))methodName=Symbol[methodName.substring(2)];if(isPureVirtual)classType.registeredClass.pureVirtualFunctions.push(methodName);function unboundTypesHandler(){throwUnboundTypeError(`Cannot call ${humanName} due to unbound types`,rawArgTypes)}var proto=classType.registeredClass.instancePrototype;var method=proto[methodName];if(void 0===method||void 0===method.overloadTable&&method.className!==classType.name&&method.argCount===argCount-2){unboundTypesHandler.argCount=argCount-2;unboundTypesHandler.className=classType.name;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-2]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{var memberFunction=craftInvokerFunction(humanName,argTypes,classType,rawInvoker,context,isAsync);if(void 0===proto[methodName].overloadTable){memberFunction.argCount=argCount-2;proto[methodName]=memberFunction}else proto[methodName].overloadTable[argCount-2]=memberFunction;return[]});return[]})},_embind_register_emval:rawType=>registerType(rawType,EmValType),_embind_register_float:(rawType,name,size)=>{name=AsciiToString(name);registerType(rawType,{name,fromWireType:value=>value,toWireType:(destructors,value)=>value,readValueFromPointer:floatReadValueFromPointer(name,size),destructorFunction:null})},_embind_register_integer:(primitiveType,name,size,minRange,maxRange)=>{name=AsciiToString(name);const isUnsignedType=minRange===0;let fromWireType=value=>value;if(isUnsignedType){var bitshift=32-8*size;fromWireType=value=>value<>>bitshift;maxRange=fromWireType(maxRange)}registerType(primitiveType,{name,fromWireType,toWireType:(destructors,value)=>value,readValueFromPointer:integerReadValueFromPointer(name,size,minRange!==0),destructorFunction:null})},_embind_register_memory_view:(rawType,dataTypeIndex,name)=>{var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,BigInt64Array,BigUint64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){var size=HEAPU32[handle>>2];var data=HEAPU32[handle+4>>2];return new TA(HEAP8.buffer,data,size)}name=AsciiToString(name);registerType(rawType,{name,fromWireType:decodeMemoryView,readValueFromPointer:decodeMemoryView},{ignoreDuplicateRegistrations:true})},_embind_register_std_string:(rawType,name)=>{name=AsciiToString(name);registerType(rawType,{name,fromWireType(value){var length=HEAPU32[value>>2];var payload=value+4;var str;if(true)str=UTF8ToString(payload,length,true);else{str="";for(var i=0;i>2]=length;if(valueIsOfTypeString)if(true)stringToUTF8(value,ptr,length+1);else for(var i=0;i255){_free(base);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+i]=charCode}else HEAPU8.set(value,ptr);if(destructors!==null)destructors.push(_free,base);return base},readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})},_embind_register_std_wstring:(rawType,charSize,name)=>{name=AsciiToString(name);var decodeString,encodeString,lengthBytesUTF;if(charSize===2){decodeString=UTF16ToString;encodeString=stringToUTF16;lengthBytesUTF=lengthBytesUTF16}else{decodeString=UTF32ToString;encodeString=stringToUTF32;lengthBytesUTF=lengthBytesUTF32}registerType(rawType,{name,fromWireType:value=>{var length=HEAPU32[value>>2];var str=decodeString(value+4,length*charSize,true);_free(value);return str},toWireType:(destructors,value)=>{if(!(typeof value==="string"))throwBindingError(`Cannot pass non-string to C++ string type ${name}`);var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length/charSize;encodeString(value,ptr+4,length+charSize);if(destructors!==null)destructors.push(_free,ptr);return ptr},readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})},_embind_register_void:(rawType,name)=>{name=AsciiToString(name);registerType(rawType,{isVoid:true,name,fromWireType:()=>{},toWireType:()=>{}})},_emval_create_invoker:(argCount,argTypesPtr,kind)=>{var[retType,...argTypes]=emval_lookupTypes(argCount,argTypesPtr);var toReturnWire=retType.toWireType.bind(retType);var argFromPtr=argTypes.map(type=>type.readValueFromPointer.bind(type));argCount--;var captures={toValue:Emval.toValue};var args=argFromPtr.map((argFromPtr,i)=>{var captureName=`argFromPtr${i}`;captures[captureName]=argFromPtr;return`${captureName}(args${i?"+"+i*8:""})`});var functionBody;switch(kind){case 0:functionBody="toValue(handle)";break;case 2:functionBody="new (toValue(handle))";break;case 3:functionBody="";break;case 1:captures["getStringOrSymbol"]=getStringOrSymbol;functionBody="toValue(handle)[getStringOrSymbol(methodName)]";break}functionBody+=`(${args})`;if(!retType.isVoid){captures["toReturnWire"]=toReturnWire;captures["emval_returnValue"]=emval_returnValue;functionBody=`return emval_returnValue(toReturnWire, destructorsRef, ${functionBody})`}functionBody=`return function (handle, methodName, destructorsRef, args) {\n${functionBody}\n}`;var invokerFunction=new Function(Object.keys(captures),functionBody)(...Object.values(captures));var functionName=`methodCaller<(${argTypes.map(t=>t.name)}) => ${retType.name}>`;return emval_addMethodCaller(createNamedFunction(functionName,invokerFunction))},_emval_decref:__emval_decref,_emval_get_property:(handle,key)=>{handle=Emval.toValue(handle);key=Emval.toValue(key);return Emval.toHandle(handle[key])},_emval_incref:handle=>{if(handle>9)emval_handles[handle+1]+=1},_emval_invoke:(caller,handle,methodName,destructorsRef,args)=>emval_methodCallers[caller](handle,methodName,destructorsRef,args),_emval_new_array:()=>Emval.toHandle([]),_emval_new_cstring:v=>Emval.toHandle(getStringOrSymbol(v)),_emval_new_object:()=>Emval.toHandle({}),_emval_run_destructors:handle=>{var destructors=Emval.toValue(handle);runDestructors(destructors);__emval_decref(handle)},_emval_set_property:(handle,key,value)=>{handle=Emval.toValue(handle);key=Emval.toValue(key);value=Emval.toValue(value);handle[key]=value},_tzset_js:(timezone,daylight,std_name,dst_name)=>{var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);var winterOffset=winter.getTimezoneOffset();var summerOffset=summer.getTimezoneOffset();var stdTimezoneOffset=Math.max(winterOffset,summerOffset);HEAPU32[timezone>>2]=stdTimezoneOffset*60;HEAP32[daylight>>2]=Number(winterOffset!=summerOffset);var extractZone=timezoneOffset=>{var sign=timezoneOffset>=0?"-":"+";var absOffset=Math.abs(timezoneOffset);var hours=String(Math.floor(absOffset/60)).padStart(2,"0");var minutes=String(absOffset%60).padStart(2,"0");return`UTC${sign}${hours}${minutes}`};var winterName=extractZone(winterOffset);var summerName=extractZone(summerOffset);if(summerOffset>3]=BigInt(nsec);return 0},emscripten_get_heap_max:()=>getHeapMax(),emscripten_resize_heap:requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize)return false;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement)return true}return false},environ_get:(__environ,environ_buf)=>{var bufSize=0;var envp=0;for(var string of getEnvStrings()){var ptr=environ_buf+bufSize;HEAPU32[__environ+envp>>2]=ptr;bufSize+=stringToUTF8(string,ptr,1/0)+1;envp+=4}return 0},environ_sizes_get:(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;for(var string of strings)bufSize+=lengthBytesUTF8(string)+1;HEAPU32[penviron_buf_size>>2]=bufSize;return 0},fd_close:function(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS==="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}},fd_read:function(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doReadv(stream,iov,iovcnt);HEAPU32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}},fd_seek:function(fd,offset,whence,newOffset){offset=bigintToI53Checked(offset);try{if(isNaN(offset))return 22;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);HEAP64[newOffset>>3]=BigInt(stream.position);if(stream.getdents&&!offset&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS==="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}},fd_write:function(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doWritev(stream,iov,iovcnt);HEAPU32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}};var wasmExports;wasmExports=await async function(){function receiveInstance(instance){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}var info=getWasmImports();var instantiateWasm=Module["instantiateWasm"];if(instantiateWasm)return new Promise(resolve=>{instantiateWasm(info,inst=>resolve(receiveInstance(inst)))});wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(void 0,wasmBinaryFile,info);var exports=function(result){return receiveInstance(result["instance"])}(result);return exports}();await async function(){preRun();if(runDependencies)await resolveRunDependencies();var setStatus=Module["setStatus"];if(setStatus){setStatus("Running...");await new Promise(resolve=>setTimeout(resolve,1));setTimeout(setStatus,1,"")}if(ABORT)return;initRuntime();Module["onRuntimeInitialized"]?.();postRun()}(); +;return Module}export default createTinyCpu; diff --git a/web/public/tiny_cpu.wasm b/web/public/tiny_cpu.wasm new file mode 100755 index 0000000..56f00a7 Binary files /dev/null and b/web/public/tiny_cpu.wasm differ diff --git a/web/src/components/AsmEditor.tsx b/web/src/components/AsmEditor.tsx new file mode 100644 index 0000000..d814185 --- /dev/null +++ b/web/src/components/AsmEditor.tsx @@ -0,0 +1,59 @@ +import { useRef } from "react"; +import type { AsmError } from "../lib/assembler"; + +interface Props { + value: string; + onChange: (v: string) => void; + errors: AsmError[]; + /** 1-based source line of the instruction executing now, if any */ + activeLine?: number; +} + +const LINE_HEIGHT = "1.5rem"; + +export function AsmEditor({ value, onChange, errors, activeLine }: Props) { + const gutter = useRef(null); + const lines = value.split("\n").length; + const errorLines = new Map(errors.map((e) => [e.line, e.message])); + + return ( +
+
+ {Array.from({ length: lines }, (_, i) => { + const n = i + 1; + const isErr = errorLines.has(n); + const isActive = n === activeLine; + return ( +
+ {n} +
+ ); + })} +
+