From f646a303fc41cfd76cfc4e690bc5e96f24498bed Mon Sep 17 00:00:00 2001 From: fullzer4 Date: Sun, 14 Sep 2025 17:30:06 -0300 Subject: [PATCH 1/2] feat(arch): rust-vmm arch --- .gitattributes | 1 - .github/workflows/deploy-docs.yml | 39 - .gitignore | 56 +- Cargo.lock | 430 ++ Cargo.toml | 63 +- README.md | 166 +- docker/Dockerfile.python-basic | 32 - docs/astro.config.mjs | 65 - docs/package-lock.json | 6305 ----------------- docs/package.json | 16 - docs/src/content/config.ts | 8 - docs/src/content/docs/api.mdx | 29 - docs/src/content/docs/changelog.mdx | 7 - docs/src/content/docs/contributing.mdx | 8 - docs/src/content/docs/examples.mdx | 18 - docs/src/content/docs/guides/isolation.mdx | 11 - docs/src/content/docs/index.mdx | 80 - docs/src/content/docs/install.mdx | 29 - docs/src/content/docs/internals.mdx | 8 - docs/src/content/docs/license.mdx | 5 - docs/src/content/docs/quickstart.mdx | 29 - .../src/content/docs/reference/host-tools.mdx | 9 - docs/src/content/docs/reference/result.mdx | 19 - .../src/content/docs/reference/transports.mdx | 8 - docs/src/content/docs/troubleshooting.mdx | 8 - docs/src/content/docs/usage/artifacts.mdx | 15 - docs/src/content/docs/usage/image.mdx | 14 - docs/src/env.d.ts | 1 - docs/tsconfig.json | 7 - examples/basic_example.py | 51 - flake.lock | 6 +- flake.nix | 204 +- flashvm/__init__.py | 4 - ...ee25fdb599d9d3988b45da3978d7025c623db11909 | 3 - ...53d9dc3e25bbeba1744fc3a79ba52493c28ed21d56 | 3 - ...3aa6975cf84ae2b4b9258d6cda7a2a7d39971f30f5 | 3 - ...14d944a9cd4454508c0adfdac240e9e81092fa831c | 3 - ...af1c7dbebad6d22895fb3e68d4834fc18286de2d2a | 3 - ...994a43a221bf31b672d81fa2140e8b80a1c6c9b227 | 3 - ...fecbfd6437a517df8b0c26c1682054a102182a6dc5 | 3 - flashvm/data/oci/index.json | 3 - flashvm/data/oci/oci-layout | 3 - pyproject.toml | 120 +- python/flashvm/__init__.py | 4 + src/config.rs | 108 - src/error.rs | 38 +- src/image_resolver.rs | 286 - src/lib.rs | 333 +- src/types.rs | 81 + src/vm_runner.rs | 460 -- src/vmm/boot.rs | 25 + src/vmm/devices.rs | 18 + src/vmm/event_loop.rs | 13 + src/vmm/kvm_ctx.rs | 20 + src/vmm/memory.rs | 14 + src/vmm/mod.rs | 8 + src/vmm/platform_aarch64.rs | 4 + src/vmm/platform_x86.rs | 4 + src/vmm/run.rs | 36 + src/wheel_resources.rs | 74 - tests/__init__.py | 1 - tests/benchmark/test_performance.py | 375 - tests/conftest.py | 240 - tests/integration/test_complex_scenarios.py | 487 -- tests/unit/test_basic_api.py | 301 - tests/unit/test_edge_cases.py | 413 -- tests/unit/test_wheel_resources.py | 14 - uv.lock | 684 +- 68 files changed, 829 insertions(+), 11110 deletions(-) delete mode 100644 .gitattributes delete mode 100644 .github/workflows/deploy-docs.yml create mode 100644 Cargo.lock delete mode 100644 docker/Dockerfile.python-basic delete mode 100644 docs/astro.config.mjs delete mode 100644 docs/package-lock.json delete mode 100644 docs/package.json delete mode 100644 docs/src/content/config.ts delete mode 100644 docs/src/content/docs/api.mdx delete mode 100644 docs/src/content/docs/changelog.mdx delete mode 100644 docs/src/content/docs/contributing.mdx delete mode 100644 docs/src/content/docs/examples.mdx delete mode 100644 docs/src/content/docs/guides/isolation.mdx delete mode 100644 docs/src/content/docs/index.mdx delete mode 100644 docs/src/content/docs/install.mdx delete mode 100644 docs/src/content/docs/internals.mdx delete mode 100644 docs/src/content/docs/license.mdx delete mode 100644 docs/src/content/docs/quickstart.mdx delete mode 100644 docs/src/content/docs/reference/host-tools.mdx delete mode 100644 docs/src/content/docs/reference/result.mdx delete mode 100644 docs/src/content/docs/reference/transports.mdx delete mode 100644 docs/src/content/docs/troubleshooting.mdx delete mode 100644 docs/src/content/docs/usage/artifacts.mdx delete mode 100644 docs/src/content/docs/usage/image.mdx delete mode 100644 docs/src/env.d.ts delete mode 100644 docs/tsconfig.json delete mode 100644 examples/basic_example.py delete mode 100644 flashvm/__init__.py delete mode 100644 flashvm/data/oci/blobs/sha256/39dbf954503055210a0ccbee25fdb599d9d3988b45da3978d7025c623db11909 delete mode 100644 flashvm/data/oci/blobs/sha256/89b2a69cdaa732c456834d53d9dc3e25bbeba1744fc3a79ba52493c28ed21d56 delete mode 100644 flashvm/data/oci/blobs/sha256/ca9e0bf09fc325818b461d3aa6975cf84ae2b4b9258d6cda7a2a7d39971f30f5 delete mode 100644 flashvm/data/oci/blobs/sha256/e4ba4d6f4188ade48f670414d944a9cd4454508c0adfdac240e9e81092fa831c delete mode 100644 flashvm/data/oci/blobs/sha256/f34d2f7d34ea07609ad343af1c7dbebad6d22895fb3e68d4834fc18286de2d2a delete mode 100644 flashvm/data/oci/blobs/sha256/f716fb09bc521d3afbb665994a43a221bf31b672d81fa2140e8b80a1c6c9b227 delete mode 100644 flashvm/data/oci/blobs/sha256/fbc8c3c9054a9d4f998cb1fecbfd6437a517df8b0c26c1682054a102182a6dc5 delete mode 100644 flashvm/data/oci/index.json delete mode 100644 flashvm/data/oci/oci-layout create mode 100644 python/flashvm/__init__.py delete mode 100644 src/config.rs delete mode 100644 src/image_resolver.rs create mode 100644 src/types.rs delete mode 100644 src/vm_runner.rs create mode 100644 src/vmm/boot.rs create mode 100644 src/vmm/devices.rs create mode 100644 src/vmm/event_loop.rs create mode 100644 src/vmm/kvm_ctx.rs create mode 100644 src/vmm/memory.rs create mode 100644 src/vmm/mod.rs create mode 100644 src/vmm/platform_aarch64.rs create mode 100644 src/vmm/platform_x86.rs create mode 100644 src/vmm/run.rs delete mode 100644 src/wheel_resources.rs delete mode 100644 tests/__init__.py delete mode 100644 tests/benchmark/test_performance.py delete mode 100644 tests/conftest.py delete mode 100644 tests/integration/test_complex_scenarios.py delete mode 100644 tests/unit/test_basic_api.py delete mode 100644 tests/unit/test_edge_cases.py delete mode 100644 tests/unit/test_wheel_resources.py diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index fa6a59a..0000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -flashvm/data/oci/** filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml deleted file mode 100644 index 5314a33..0000000 --- a/.github/workflows/deploy-docs.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Deploy Docs - -on: - push: - branches: [ main ] - workflow_dispatch: - -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: "pages" - cancel-in-progress: true - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Build with Astro - uses: withastro/action@v4 - with: - path: docs - node-version: 20 - package-manager: npm - - deploy: - needs: build - runs-on: ubuntu-latest - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 3d1795f..ea8c4bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,55 +1 @@ -# Rust -/target/ -Cargo.lock - -# Python -__pycache__/ -*.py[cod] -*$py.class -*.so -*.egg-info/ -dist/ -build/ -.tox/ -.venv/ -venv/ -.pytest_cache/ - -# Coverage reports -htmlcov/ -.coverage -.coverage.* -coverage.xml -coverage.json -reports/ -.nyc_output - -# IDEs -.vscode/ -.idea/ -*.swp -*.swo -*~ - -# OS -.DS_Store -Thumbs.db - -# Cache específico do projeto -.cache/ -*.log - -# Maturin -target/ -.maturin/ - -# Temp files -*.tmp -*.temp - -# Benchmarks -.benchmarks/ - -# Docs -node_modules/ -.astro/ \ No newline at end of file +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..0a10c1e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,430 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "acpi_tables" +version = "0.1.0" +source = "git+https://github.com/rust-vmm/acpi_tables#e08a3f0b0a59b98859dbf59f5aa7fd4d2eb4018a" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "anyhow" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" + +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "flashvm" +version = "0.1.0" +dependencies = [ + "acpi_tables", + "anyhow", + "kvm-bindings", + "kvm-ioctls", + "linux-loader", + "pyo3", + "serde", + "serde_json", + "thiserror", + "virtio-device", + "virtio-queue", + "vm-fdt", + "vm-memory", + "vm-superio", + "vmm-sys-util", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indoc" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "kvm-bindings" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b3c06ff73c7ce03e780887ec2389d62d2a2a9ddf471ab05c2ff69207cd3f3b4" +dependencies = [ + "vmm-sys-util", +] + +[[package]] +name = "kvm-ioctls" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "333f77a20344a448f3f70664918135fddeb804e938f28a99d685bd92926e0b19" +dependencies = [ + "bitflags 2.9.4", + "kvm-bindings", + "libc", + "vmm-sys-util", +] + +[[package]] +name = "libc" +version = "0.2.175" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + +[[package]] +name = "linux-loader" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870c3814345f050991f99869417779f6062542bcf4ed81db7a1b926ad1306638" +dependencies = [ + "vm-memory", +] + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pyo3" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f402062616ab18202ae8319da13fa4279883a2b8a9d9f83f20dbade813ce1884" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "memoffset", + "once_cell", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b14b5775b5ff446dd1056212d778012cbe8a0fbffd368029fd9e25b514479c38" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ab5bcf04a2cdcbb50c7d6105de943f543f9ed92af55818fd17b660390fc8636" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fd24d897903a9e6d80b968368a34e1525aeb719d568dba8b3d4bfa5dc67d453" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe" +dependencies = [ + "heck", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "serde" +version = "1.0.222" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aab69e3f5be1836a1fe0aca0b286e5a5b38f262d6c9e8acd2247818751fcc8fb" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.222" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f8ebec5eea07db7df9342aa712db2138f019d9ab3454a60a680579a6f841b80" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.222" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5f61630fe26d0ff555e6c37dc445ab2f15871c8a11ace3cf471b3195d3e4f49" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" + +[[package]] +name = "unindent" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" + +[[package]] +name = "virtio-bindings" +version = "0.2.6" +source = "git+https://github.com/rust-vmm/vm-virtio#57ad18a534000b7658faaeb88a384d483e80d96f" + +[[package]] +name = "virtio-device" +version = "0.1.0" +source = "git+https://github.com/rust-vmm/vm-virtio#57ad18a534000b7658faaeb88a384d483e80d96f" +dependencies = [ + "log", + "virtio-bindings", + "virtio-queue", + "vm-memory", +] + +[[package]] +name = "virtio-queue" +version = "0.16.0" +source = "git+https://github.com/rust-vmm/vm-virtio#57ad18a534000b7658faaeb88a384d483e80d96f" +dependencies = [ + "libc", + "log", + "virtio-bindings", + "vm-memory", + "vmm-sys-util", +] + +[[package]] +name = "vm-fdt" +version = "0.3.0" +source = "git+https://github.com/rust-vmm/vm-fdt#ef5bd734f5f66fb07722d766981adbc915f0d941" + +[[package]] +name = "vm-memory" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd5e56d48353c5f54ef50bd158a0452fc82f5383da840f7b8efc31695dd3b9d" +dependencies = [ + "libc", + "thiserror", + "winapi", +] + +[[package]] +name = "vm-superio" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3428ee25acbfc75ed14600f2043876e0889cbd57c39dd441191417377cdceda0" + +[[package]] +name = "vmm-sys-util" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "506c62fdf617a5176827c2f9afbcf1be155b03a9b4bf9617a60dbc07e3a1642f" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index f6ab5cc..24570e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,39 +1,42 @@ [package] name = "flashvm" -version = "0.1.1" +version = "0.1.0" edition = "2021" -description = "Run Python code inside libkrun microVMs, with an embedded offline OCI image" -license = "MIT" -repository = "https://github.com/fullzer4/flashvm" -homepage = "https://github.com/fullzer4/flashvm" -documentation = "https://github.com/fullzer4/flashvm#readme" -include = [ - "Cargo.toml", - "src/**", - "pyproject.toml", - "README.md", - "flashvm/**", - "examples/**", - "docker/**", -] +license = "Apache-2.0" +description = "Python-first microVM runner using rust-vmm, for running OCI-prepared Python code in isolated VMs" + [lib] -name = "flashvm" +name = "flashvm_native" crate-type = ["cdylib"] + +[features] +x86_64 = ["acpi_tables"] +aarch64 = ["vm-fdt"] + + [dependencies] pyo3 = { version = "0.22", features = ["extension-module", "abi3-py38"] } -tokio = { version = "1.0", features = ["full"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -anyhow = "1.0" -log = "0.4" -tempfile = "3.0" -uuid = { version = "1.0", features = ["v4"] } -sha256 = "1.0" -chrono = { version = "0.4", features = ["serde"] } -glob = "0.3" - -[profile.release] -codegen-units = 1 -lto = "thin" +anyhow = "1" +thiserror = "1" +serde = { version = "1", features = ["derive"] } +serde_json = "1" + + +kvm-ioctls = "*" # TODO: fixar versões +kvm-bindings = "*" +vm-memory = { version = "*", features = ["backend-mmap"] } +linux-loader = "*" +vm-superio = "*" +vmm-sys-util = "*" + +# These rust-vmm crates are not published to crates.io; use git and keep them optional. +# They are enabled via the corresponding arch features above. +acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", optional = true } +vm-fdt = { git = "https://github.com/rust-vmm/vm-fdt", optional = true } + +# Virtio crates are a workspace; depend on specific crates by git + package name +virtio-device = { git = "https://github.com/rust-vmm/vm-virtio", package = "virtio-device" } +virtio-queue = { git = "https://github.com/rust-vmm/vm-virtio", package = "virtio-queue" } +# virtio-blk = { git = "https://github.com/rust-vmm/vm-virtio", package = "virtio-blk" } \ No newline at end of file diff --git a/README.md b/README.md index cc54d07..3649292 100644 --- a/README.md +++ b/README.md @@ -1,126 +1,112 @@ -# flashVM +# flashvm -[![PyPI](https://img.shields.io/pypi/v/flashvm?label=PyPI)](https://pypi.org/project/flashvm/) -[![Python Versions](https://img.shields.io/pypi/pyversions/flashvm.svg)](https://pypi.org/project/flashvm/) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Downloads](https://static.pepy.tech/badge/flashvm)](https://pepy.tech/project/flashvm) +**Python‑first microVM runner** built on **rust‑vmm** to execute Python code inside isolated VMs from a **prepared OCI image** (or an embedded cached image). -Run short Python snippets in a microVM for strong isolation—without asking users to pull images or learn container tooling. On first run, flashVM imports an embedded OCI image into local containers-storage, and then boots a microVM via krunvm (libkrun). The result (stdout, exit code, and optional artifacts) is returned to your Python process. +> Minimal Python API: +> +> - `prepare_image(meta: dict) -> ImageHandle` +> - `run(image: ImageHandle, code: str, opts: dict | None) -> RunResult` -**Under the hood:** krunvm is a CLI for creating microVMs from OCI images using libkrun and buildah; it targets Linux/KVM. -The embedded image uses standard OCI transports; tools like skopeo can copy oci: layouts into containers-storage: for local use. +--- -## Why flashVM? +## Features -- **Real isolation:** Code executes inside a tiny KVM-backed microVM (via libkrun). -- **Zero setup for images:** A minimal Python image ships inside the wheel (OCI layout) and is auto-imported on first run. -- **Friendly API:** One call in Python (`flashvm.run(...)`) returns stdout/stderr, exit code, and optional output files (“artifacts”). - -## How it works - -```mermaid -flowchart LR - A[Your Python code] --> B[flashVM] - B --> C[krunvm] - C --> D[MicroVM] - D --> E[stdout / artifacts] - - %% optional first-run step (imports the embedded image) - B -. first run .-> F[(Embedded OCI image)] - F --> G[/containers-storage/] - G --> C -``` - -> krunvm: “CLI-based utility for creating microVMs from OCI images, using libkrun and buildah” and targeting Linux/KVM. -> OCI transports: oci:PATH[:ref] and containers-storage:… are standard transports; skopeo copy can move images between them (e.g., oci: → containers-storage:). +- **Strong isolation** via KVM: read‑only rootfs + per‑run writable workspace. +- **Prepared image cache**: install `pip` deps during *prepare* and reuse across runs. +- **No external daemon**: a minimal Rust VMM (rust‑vmm crates), surfaced via PyO3. +- **Linux‑only** (x86_64 and aarch64 with KVM). ## Requirements -### Host OS / hardware -- Linux with KVM available (i.e., /dev/kvm exists), because krunvm/libkrun rely on KVM. +- Linux with **KVM** enabled (`/dev/kvm` accessible). +- Guest kernel (`vmlinux`/`bzImage`) and a minimal **guest init** script (you can ship them under `python/flashvm/assets/`). +- Python ≥ 3.8. -### System tools (installed on the host) -- **krunvm** – microVM launcher. -- **buildah** – used by krunvm and for rootless image operations. -- **skopeo** (optional but recommended) – for fast, policy-aware copying of the embedded oci: layout into local containers-storage: on first run. +> Tip: add your user to the `kvm` group (and re‑login) to avoid running as root. -> Package names are typically krunvm, buildah, and skopeo on mainstream distros. See your distribution’s repositories or the krunvm project page for install options. +--- -### Python -- Python 3.8+. +## Quickstart -## Installation +### 1) Prepare image (cache) -From PyPI: -```bash -pip install flashvm -``` +The *prepare* phase happens on the Python side: you resolve the OCI image (or use an embedded one), install `pip` deps, and hand paths to the native module. -From source (editable dev mode): -```bash -pip install maturin -maturin develop +```python +import flashvm as fv + +img = fv.prepare_image({ + "kernel": "/path/to/vmlinux", + "rootfs_img": "/path/to/rootfs.img", # ext4, squashfs, etc. + # "initrd": "/path/to/initrd", # optional + # "cache_key": "python312-alpine+numpy1.26", +}) ``` -> krunvm/buildah/skopeo are host tools, installed via your OS package manager. See the krunvm repository for up-to-date guidance. +> Suggestion: keep a stable *cache key* per base OCI + installed `pip` deps. -## Quick start +### 2) Run isolated code ```python -import flashvm as fvm - -# Optional: import the embedded OCI image into containers-storage now (idempotent) -fvm.prepare_image() # First run does this automatically if you skip it. - -# Run a short snippet in a microVM -res = fvm.run("print('Hello from microVM!')") -print("exit:", res["exit_code"]) -print("stdout:", res["stdout"]) -print("stderr:", res["stderr"]) -print("image_used:", res["image_used"]) +res = fv.run( + img, + code="import platform; print('hello', platform.python_version())", + opts={ + "cpus": 1, + "mem_mb": 512, + "timeout_ms": 15000, + "output": "diff", # diff|all|none|paths + }, +) +print(res.exit_status) +print(res.stdout) +print("artifacts:", res.outputs_dir) ``` -> Example with artifacts: +> `run` creates a clean **workspace** (RW disk) per execution and returns `stdout/stderr`, `exit_status`, and a directory with collected artifacts per the chosen output mode. -```python -code = r""" -with open('/work/out/result.txt', 'w') as f: - f.write('ok\n') -print('done') -""" - -res = fvm.run(code, expect=["out/*.txt"]) -for a in res["artifacts"]: - print(a["guest_path"], a["size_bytes"]) -``` +--- -## What ships in the wheel? +## How it works (technical sketch) -An embedded OCI image layout (minimal Python userspace) placed inside the package data. +- **kvm-ioctls / kvm-bindings**: create VM and vCPUs safely. +- **vm-memory**: manage guest physical memory mappings. +- **linux-loader**: load kernel + cmdline (and `initrd` if present). +- **vm-superio (serial)**: `ttyS0` console for logs / `stdout`. +- **vm-virtio (blk)**: two virtio‑blk disks — `vda` (rootfs, RO) and `vdb` (workspace, RW). +- **event-manager**: epoll‑based event loop for device I/O. +- **vmm-sys-util**: utilities (`EventFd`, ioctl helpers). +- **acpi_tables** (x86) / **vm-fdt** (arm64): boot tables when needed. -> On first use, flashVM imports that layout into local containers-storage: (using skopeo copy if present; otherwise a buildah fallback), then boots it via krunvm. +Networking is **off by default**. Enable explicitly if your workload requires it. -## Troubleshooting +--- -- **“KVM not available”** – ensure hardware virtualization is enabled in BIOS/UEFI and /dev/kvm exists (group permissions may apply). krunvm targets Linux/KVM. -- **Image/transport errors** – skopeo copy supports oci: and containers-storage: transports. If skopeo isn’t installed, flashVM falls back to buildah-based import. -- **Rootless storage** – buildah and the underlying containers stack use containers-storage; this is the local image store queried by tools (buildah images, etc.). Transport syntax and examples are documented in the containers-transports manpage. +## Prepare best practices -## Security / Isolation notes +- Build `rootfs.img` with **pip deps preinstalled** (inside a chroot/guest) to avoid ABI mismatches. +- Keep rootfs **read‑only** and a separate **workspace** for easy artifact collection and diffs. +- Prefer **wheels** (`pip --only-binary :all:`) in the prepare phase to reduce install time. -> flashVM relies on krunvm (which uses libkrun) to run each execution inside a microVM. This provides stronger isolation than plain containers while keeping startup latency low. See the krunvm project for background and supported platforms. +--- -## License +## Troubleshooting -MIT (this project). krunvm/libkrun and the containers tooling are separate projects with their own licenses. See upstream repositories for details. +- **`/dev/kvm` permission denied**: ensure your user is in the `kvm` group; re‑login after adding. +- **Missing kernel/init**: place assets under `python/flashvm/assets/` or pass absolute paths to `prepare_image`. +- **No console output**: check `console=ttyS0` in the kernel cmdline and that the serial device is mapped. +- **Timeouts**: increase `timeout_ms` or verify your `init` runs and exits correctly. -## Acknowledgments +--- -The krunvm project and libkrun for making userspace microVMs practical. +## Roadmap (high level) -The containers ecosystem (buildah, skopeo, containers-image/storage) for robust image transports—especially oci: and containers-storage:. +- Full virtio‑blk workflow (diff, path filters, artifact compression). +- Optional `seccomp` hardening of the VMM process. +- Optional "direct OCI" mode (pull + unpack) behind a build feature. -## Appendix: Useful references +--- + +## License -- krunvm project page (overview, install, supported platforms). -- containers-transports manpage (syntax for oci:… and containers-storage:…, examples with skopeo copy). +Apache‑2.0 OR BSD‑3‑Clause (dual license), in line with the rust‑vmm ecosystem. diff --git a/docker/Dockerfile.python-basic b/docker/Dockerfile.python-basic deleted file mode 100644 index 5dde149..0000000 --- a/docker/Dockerfile.python-basic +++ /dev/null @@ -1,32 +0,0 @@ -FROM python:3.12-slim - -ENV PYTHONDONTWRITEBYTECODE=1 \ - PYTHONUNBUFFERED=1 \ - PIP_NO_CACHE_DIR=1 - -RUN apt-get update && apt-get install -y --no-install-recommends \ - libgomp1 \ - fonts-dejavu-core \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /app - -RUN pip install --no-cache-dir \ - numpy \ - pandas \ - scipy \ - matplotlib \ - seaborn \ - scikit-learn \ - statsmodels \ - pyarrow - -RUN useradd -m -s /bin/bash appuser \ - && mkdir -p /home/appuser/.config/matplotlib /home/appuser/.cache/matplotlib \ - && chown -R appuser:appuser /home/appuser - -ENV MPLCONFIGDIR=/home/appuser/.config/matplotlib - -USER appuser - -CMD ["python3"] diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs deleted file mode 100644 index c6a533a..0000000 --- a/docs/astro.config.mjs +++ /dev/null @@ -1,65 +0,0 @@ -import { defineConfig } from 'astro/config'; -import starlight from '@astrojs/starlight'; - -export default defineConfig({ - site: 'https://fullzer4.github.io/flashvm', - base: '/flashvm', - contentLayer: true, - integrations: [ - starlight({ - title: 'flashVM', - tagline: 'Run Python snippets inside microVMs (libkrun/krunvm).', - description: 'Run short Python snippets in a KVM microVM via krunvm/libkrun. Ships an embedded OCI image for zero-setup.', - social: [ - { icon: 'github', label: 'GitHub', href: 'https://github.com/fullzer4/flashvm' } - ], - editLink: { - baseUrl: 'https://github.com/fullzer4/flashvm/edit/main/docs/src/content/docs/' - }, - lastUpdated: true, - sidebar: [ - { - label: 'Getting Started', - items: [ - { label: 'Overview', link: '/' }, - { label: 'Installation & Requirements', link: '/install' }, - { label: 'Quickstart', link: '/quickstart' } - ] - }, - { - label: 'Usage', - items: [ - { label: 'Python API', link: '/api' }, - { label: 'Artifacts', link: '/usage/artifacts' }, - { label: 'Embedded Image & Storage', link: '/usage/image' }, - { label: 'Troubleshooting', link: '/troubleshooting' } - ] - }, - { - label: 'Guides', - items: [ - { label: 'Examples', link: '/examples' }, - { label: 'Isolation & Security', link: '/guides/isolation' }, - { label: 'Internals', link: '/internals' } - ] - }, - { - label: 'Reference', - items: [ - { label: 'Result Schema', link: '/reference/result' }, - { label: 'Host Tooling (krunvm/buildah/skopeo)', link: '/reference/host-tools' }, - { label: 'Transport Syntax (oci:/containers-storage:)', link: '/reference/transports' } - ] - }, - { - label: 'Project', - items: [ - { label: 'Contributing', link: '/contributing' }, - { label: 'Changelog', link: '/changelog' }, - { label: 'License', link: '/license' } - ] - } - ] - }) - ] -}); diff --git a/docs/package-lock.json b/docs/package-lock.json deleted file mode 100644 index 33a00b5..0000000 --- a/docs/package-lock.json +++ /dev/null @@ -1,6305 +0,0 @@ -{ - "name": "flashvm-docs", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "flashvm-docs", - "dependencies": { - "@astrojs/mdx": "^4.3.4", - "@astrojs/starlight": "^0.34.8", - "astro": "^5.13.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@astrojs/compiler": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.12.2.tgz", - "integrity": "sha512-w2zfvhjNCkNMmMMOn5b0J8+OmUaBL1o40ipMvqcG6NRpdC+lKxmTi48DT8Xw0SzJ3AfmeFLB45zXZXtmbsjcgw==", - "license": "MIT" - }, - "node_modules/@astrojs/internal-helpers": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.2.tgz", - "integrity": "sha512-KCkCqR3Goym79soqEtbtLzJfqhTWMyVaizUi35FLzgGSzBotSw8DB1qwsu7U96ihOJgYhDk2nVPz+3LnXPeX6g==", - "license": "MIT" - }, - "node_modules/@astrojs/markdown-remark": { - "version": "6.3.6", - "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.6.tgz", - "integrity": "sha512-bwylYktCTsLMVoCOEHbn2GSUA3c5KT/qilekBKA3CBng0bo1TYjNZPr761vxumRk9kJGqTOtU+fgCAp5Vwokug==", - "license": "MIT", - "dependencies": { - "@astrojs/internal-helpers": "0.7.2", - "@astrojs/prism": "3.3.0", - "github-slugger": "^2.0.0", - "hast-util-from-html": "^2.0.3", - "hast-util-to-text": "^4.0.2", - "import-meta-resolve": "^4.1.0", - "js-yaml": "^4.1.0", - "mdast-util-definitions": "^6.0.0", - "rehype-raw": "^7.0.0", - "rehype-stringify": "^10.0.1", - "remark-gfm": "^4.0.1", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.1.2", - "remark-smartypants": "^3.0.2", - "shiki": "^3.2.1", - "smol-toml": "^1.3.4", - "unified": "^11.0.5", - "unist-util-remove-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "unist-util-visit-parents": "^6.0.1", - "vfile": "^6.0.3" - } - }, - "node_modules/@astrojs/mdx": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-4.3.4.tgz", - "integrity": "sha512-Ew3iP+6zuzzJWNEH5Qr1iknrue1heEfgmfuMpuwLaSwqlUiJQ0NDb2oxKosgWU1ROYmVf1H4KCmS6QdMWKyFjw==", - "license": "MIT", - "dependencies": { - "@astrojs/markdown-remark": "6.3.6", - "@mdx-js/mdx": "^3.1.0", - "acorn": "^8.14.1", - "es-module-lexer": "^1.6.0", - "estree-util-visit": "^2.0.0", - "hast-util-to-html": "^9.0.5", - "kleur": "^4.1.5", - "rehype-raw": "^7.0.0", - "remark-gfm": "^4.0.1", - "remark-smartypants": "^3.0.2", - "source-map": "^0.7.4", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.3" - }, - "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0" - }, - "peerDependencies": { - "astro": "^5.0.0" - } - }, - "node_modules/@astrojs/prism": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.3.0.tgz", - "integrity": "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==", - "license": "MIT", - "dependencies": { - "prismjs": "^1.30.0" - }, - "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0" - } - }, - "node_modules/@astrojs/sitemap": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.5.1.tgz", - "integrity": "sha512-uX5z52GLtQTgOe8r3jeGmFRYrFe52mdpLYJzqjvL1cdy5Kg3MLOZEvaZ/OCH0fSq0t7e50uJQ6oBMZG0ffszBg==", - "license": "MIT", - "dependencies": { - "sitemap": "^8.0.0", - "stream-replace-string": "^2.0.0", - "zod": "^3.24.4" - } - }, - "node_modules/@astrojs/starlight": { - "version": "0.34.8", - "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.34.8.tgz", - "integrity": "sha512-XuYz0TfCZhje2u1Q9FNtmTdm7/B9QP91RDI1VkPgYvDhSYlME3k8gwgcBMHnR9ASDo2p9gskrqe7t1Pub/qryg==", - "license": "MIT", - "dependencies": { - "@astrojs/markdown-remark": "^6.3.1", - "@astrojs/mdx": "^4.2.3", - "@astrojs/sitemap": "^3.3.0", - "@pagefind/default-ui": "^1.3.0", - "@types/hast": "^3.0.4", - "@types/js-yaml": "^4.0.9", - "@types/mdast": "^4.0.4", - "astro-expressive-code": "^0.41.1", - "bcp-47": "^2.1.0", - "hast-util-from-html": "^2.0.1", - "hast-util-select": "^6.0.2", - "hast-util-to-string": "^3.0.0", - "hastscript": "^9.0.0", - "i18next": "^23.11.5", - "js-yaml": "^4.1.0", - "klona": "^2.0.6", - "mdast-util-directive": "^3.0.0", - "mdast-util-to-markdown": "^2.1.0", - "mdast-util-to-string": "^4.0.0", - "pagefind": "^1.3.0", - "rehype": "^13.0.1", - "rehype-format": "^5.0.0", - "remark-directive": "^3.0.0", - "ultrahtml": "^1.6.0", - "unified": "^11.0.5", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.2" - }, - "peerDependencies": { - "astro": "^5.5.0" - } - }, - "node_modules/@astrojs/telemetry": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.0.tgz", - "integrity": "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==", - "license": "MIT", - "dependencies": { - "ci-info": "^4.2.0", - "debug": "^4.4.0", - "dlv": "^1.1.3", - "dset": "^3.1.4", - "is-docker": "^3.0.0", - "is-wsl": "^3.1.0", - "which-pm-runs": "^1.1.0" - }, - "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", - "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz", - "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@capsizecss/unpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-2.4.0.tgz", - "integrity": "sha512-GrSU71meACqcmIUxPYOJvGKF0yryjN/L1aCuE9DViCTJI7bfkjgYDPD1zbNDcINJwSSP6UaBZY9GAbYDO7re0Q==", - "license": "MIT", - "dependencies": { - "blob-to-buffer": "^1.2.8", - "cross-fetch": "^3.0.4", - "fontkit": "^2.0.2" - } - }, - "node_modules/@ctrl/tinycolor": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.1.0.tgz", - "integrity": "sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ==", - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", - "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", - "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", - "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", - "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", - "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", - "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", - "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", - "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", - "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", - "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", - "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", - "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", - "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", - "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", - "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", - "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", - "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", - "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", - "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", - "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", - "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", - "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", - "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", - "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", - "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", - "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", - "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@expressive-code/core": { - "version": "0.41.3", - "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.41.3.tgz", - "integrity": "sha512-9qzohqU7O0+JwMEEgQhnBPOw5DtsQRBXhW++5fvEywsuX44vCGGof1SL5OvPElvNgaWZ4pFZAFSlkNOkGyLwSQ==", - "license": "MIT", - "dependencies": { - "@ctrl/tinycolor": "^4.0.4", - "hast-util-select": "^6.0.2", - "hast-util-to-html": "^9.0.1", - "hast-util-to-text": "^4.0.1", - "hastscript": "^9.0.0", - "postcss": "^8.4.38", - "postcss-nested": "^6.0.1", - "unist-util-visit": "^5.0.0", - "unist-util-visit-parents": "^6.0.1" - } - }, - "node_modules/@expressive-code/plugin-frames": { - "version": "0.41.3", - "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.41.3.tgz", - "integrity": "sha512-rFQtmf/3N2CK3Cq/uERweMTYZnBu+CwxBdHuOftEmfA9iBE7gTVvwpbh82P9ZxkPLvc40UMhYt7uNuAZexycRQ==", - "license": "MIT", - "dependencies": { - "@expressive-code/core": "^0.41.3" - } - }, - "node_modules/@expressive-code/plugin-shiki": { - "version": "0.41.3", - "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.41.3.tgz", - "integrity": "sha512-RlTARoopzhFJIOVHLGvuXJ8DCEme/hjV+ZnRJBIxzxsKVpGPW4Oshqg9xGhWTYdHstTsxO663s0cdBLzZj9TQA==", - "license": "MIT", - "dependencies": { - "@expressive-code/core": "^0.41.3", - "shiki": "^3.2.2" - } - }, - "node_modules/@expressive-code/plugin-text-markers": { - "version": "0.41.3", - "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.41.3.tgz", - "integrity": "sha512-SN8tkIzDpA0HLAscEYD2IVrfLiid6qEdE9QLlGVSxO1KEw7qYvjpbNBQjUjMr5/jvTJ7ys6zysU2vLPHE0sb2g==", - "license": "MIT", - "dependencies": { - "@expressive-code/core": "^0.41.3" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", - "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", - "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", - "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", - "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", - "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", - "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", - "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", - "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", - "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", - "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", - "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.5" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", - "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", - "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.0.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", - "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", - "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", - "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", - "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.2.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", - "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", - "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "license": "MIT" - }, - "node_modules/@mdx-js/mdx": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz", - "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdx": "^2.0.0", - "acorn": "^8.0.0", - "collapse-white-space": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-util-scope": "^1.0.0", - "estree-walker": "^3.0.0", - "hast-util-to-jsx-runtime": "^2.0.0", - "markdown-extensions": "^2.0.0", - "recma-build-jsx": "^1.0.0", - "recma-jsx": "^1.0.0", - "recma-stringify": "^1.0.0", - "rehype-recma": "^1.0.0", - "remark-mdx": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.0.0", - "source-map": "^0.7.0", - "unified": "^11.0.0", - "unist-util-position-from-estree": "^2.0.0", - "unist-util-stringify-position": "^4.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@oslojs/encoding": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", - "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", - "license": "MIT" - }, - "node_modules/@pagefind/darwin-arm64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.4.0.tgz", - "integrity": "sha512-2vMqkbv3lbx1Awea90gTaBsvpzgRs7MuSgKDxW0m9oV1GPZCZbZBJg/qL83GIUEN2BFlY46dtUZi54pwH+/pTQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@pagefind/darwin-x64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.4.0.tgz", - "integrity": "sha512-e7JPIS6L9/cJfow+/IAqknsGqEPjJnVXGjpGm25bnq+NPdoD3c/7fAwr1OXkG4Ocjx6ZGSCijXEV4ryMcH2E3A==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@pagefind/default-ui": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/default-ui/-/default-ui-1.4.0.tgz", - "integrity": "sha512-wie82VWn3cnGEdIjh4YwNESyS1G6vRHwL6cNjy9CFgNnWW/PGRjsLq300xjVH5sfPFK3iK36UxvIBymtQIEiSQ==", - "license": "MIT" - }, - "node_modules/@pagefind/freebsd-x64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.4.0.tgz", - "integrity": "sha512-WcJVypXSZ+9HpiqZjFXMUobfFfZZ6NzIYtkhQ9eOhZrQpeY5uQFqNWLCk7w9RkMUwBv1HAMDW3YJQl/8OqsV0Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@pagefind/linux-arm64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.4.0.tgz", - "integrity": "sha512-PIt8dkqt4W06KGmQjONw7EZbhDF+uXI7i0XtRLN1vjCUxM9vGPdtJc2mUyVPevjomrGz5M86M8bqTr6cgDp1Uw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@pagefind/linux-x64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.4.0.tgz", - "integrity": "sha512-z4oddcWwQ0UHrTHR8psLnVlz6USGJ/eOlDPTDYZ4cI8TK8PgwRUPQZp9D2iJPNIPcS6Qx/E4TebjuGJOyK8Mmg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@pagefind/windows-x64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.4.0.tgz", - "integrity": "sha512-NkT+YAdgS2FPCn8mIA9bQhiBs+xmniMGq1LFPDhcFn0+2yIUEiIG06t7bsZlhdjknEQRTSdT7YitP6fC5qwP0g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/pluginutils": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", - "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.0.tgz", - "integrity": "sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.0.tgz", - "integrity": "sha512-2O73dR4Dc9bp+wSYhviP6sDziurB5/HCym7xILKifWdE9UsOe2FtNcM+I4xZjKrfLJnq5UR8k9riB87gauiQtw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.0.tgz", - "integrity": "sha512-vwSXQN8T4sKf1RHr1F0s98Pf8UPz7pS6P3LG9NSmuw0TVh7EmaE+5Ny7hJOZ0M2yuTctEsHHRTMi2wuHkdS6Hg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.0.tgz", - "integrity": "sha512-cQp/WG8HE7BCGyFVuzUg0FNmupxC+EPZEwWu2FCGGw5WDT1o2/YlENbm5e9SMvfDFR6FRhVCBePLqj0o8MN7Vw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.0.tgz", - "integrity": "sha512-UR1uTJFU/p801DvvBbtDD7z9mQL8J80xB0bR7DqW7UGQHRm/OaKzp4is7sQSdbt2pjjSS72eAtRh43hNduTnnQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.0.tgz", - "integrity": "sha512-G/DKyS6PK0dD0+VEzH/6n/hWDNPDZSMBmqsElWnCRGrYOb2jC0VSupp7UAHHQ4+QILwkxSMaYIbQ72dktp8pKA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.0.tgz", - "integrity": "sha512-u72Mzc6jyJwKjJbZZcIYmd9bumJu7KNmHYdue43vT1rXPm2rITwmPWF0mmPzLm9/vJWxIRbao/jrQmxTO0Sm9w==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.0.tgz", - "integrity": "sha512-S4UefYdV0tnynDJV1mdkNawp0E5Qm2MtSs330IyHgaccOFrwqsvgigUD29uT+B/70PDY1eQ3t40+xf6wIvXJyg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.0.tgz", - "integrity": "sha512-1EhkSvUQXJsIhk4msxP5nNAUWoB4MFDHhtc4gAYvnqoHlaL9V3F37pNHabndawsfy/Tp7BPiy/aSa6XBYbaD1g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.0.tgz", - "integrity": "sha512-EtBDIZuDtVg75xIPIK1l5vCXNNCIRM0OBPUG+tbApDuJAy9mKago6QxX+tfMzbCI6tXEhMuZuN1+CU8iDW+0UQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.0.tgz", - "integrity": "sha512-BGYSwJdMP0hT5CCmljuSNx7+k+0upweM2M4YGfFBjnFSZMHOLYR0gEEj/dxyYJ6Zc6AiSeaBY8dWOa11GF/ppQ==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.0.tgz", - "integrity": "sha512-I1gSMzkVe1KzAxKAroCJL30hA4DqSi+wGc5gviD0y3IL/VkvcnAqwBf4RHXHyvH66YVHxpKO8ojrgc4SrWAnLg==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.0.tgz", - "integrity": "sha512-bSbWlY3jZo7molh4tc5dKfeSxkqnf48UsLqYbUhnkdnfgZjgufLS/NTA8PcP/dnvct5CCdNkABJ56CbclMRYCA==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.0.tgz", - "integrity": "sha512-LSXSGumSURzEQLT2e4sFqFOv3LWZsEF8FK7AAv9zHZNDdMnUPYH3t8ZlaeYYZyTXnsob3htwTKeWtBIkPV27iQ==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.0.tgz", - "integrity": "sha512-CxRKyakfDrsLXiCyucVfVWVoaPA4oFSpPpDwlMcDFQvrv3XY6KEzMtMZrA+e/goC8xxp2WSOxHQubP8fPmmjOQ==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.0.tgz", - "integrity": "sha512-8PrJJA7/VU8ToHVEPu14FzuSAqVKyo5gg/J8xUerMbyNkWkO9j2ExBho/68RnJsMGNJq4zH114iAttgm7BZVkA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.0.tgz", - "integrity": "sha512-SkE6YQp+CzpyOrbw7Oc4MgXFvTw2UIBElvAvLCo230pyxOLmYwRPwZ/L5lBe/VW/qT1ZgND9wJfOsdy0XptRvw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.0.tgz", - "integrity": "sha512-PZkNLPfvXeIOgJWA804zjSFH7fARBBCpCXxgkGDRjjAhRLOR8o0IGS01ykh5GYfod4c2yiiREuDM8iZ+pVsT+Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.0.tgz", - "integrity": "sha512-q7cIIdFvWQoaCbLDUyUc8YfR3Jh2xx3unO8Dn6/TTogKjfwrax9SyfmGGK6cQhKtjePI7jRfd7iRYcxYs93esg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.0.tgz", - "integrity": "sha512-XzNOVg/YnDOmFdDKcxxK410PrcbcqZkBmz+0FicpW5jtjKQxcW1BZJEQOF0NJa6JO7CZhett8GEtRN/wYLYJuw==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.0.tgz", - "integrity": "sha512-xMmiWRR8sp72Zqwjgtf3QbZfF1wdh8X2ABu3EaozvZcyHJeU0r+XAnXdKgs4cCAp6ORoYoCygipYP1mjmbjrsg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@shikijs/core": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.12.2.tgz", - "integrity": "sha512-L1Safnhra3tX/oJK5kYHaWmLEBJi1irASwewzY3taX5ibyXyMkkSDZlq01qigjryOBwrXSdFgTiZ3ryzSNeu7Q==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.12.2", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4", - "hast-util-to-html": "^9.0.5" - } - }, - "node_modules/@shikijs/engine-javascript": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.12.2.tgz", - "integrity": "sha512-Nm3/azSsaVS7hk6EwtHEnTythjQfwvrO5tKqMlaH9TwG1P+PNaR8M0EAKZ+GaH2DFwvcr4iSfTveyxMIvXEHMw==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.12.2", - "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^4.3.3" - } - }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.12.2.tgz", - "integrity": "sha512-hozwnFHsLvujK4/CPVHNo3Bcg2EsnG8krI/ZQ2FlBlCRpPZW4XAEQmEwqegJsypsTAN9ehu2tEYe30lYKSZW/w==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.12.2", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "node_modules/@shikijs/langs": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.12.2.tgz", - "integrity": "sha512-bVx5PfuZHDSHoBal+KzJZGheFuyH4qwwcwG/n+MsWno5cTlKmaNtTsGzJpHYQ8YPbB5BdEdKU1rga5/6JGY8ww==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.12.2" - } - }, - "node_modules/@shikijs/themes": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.12.2.tgz", - "integrity": "sha512-fTR3QAgnwYpfGczpIbzPjlRnxyONJOerguQv1iwpyQZ9QXX4qy/XFQqXlf17XTsorxnHoJGbH/LXBvwtqDsF5A==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.12.2" - } - }, - "node_modules/@shikijs/types": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.12.2.tgz", - "integrity": "sha512-K5UIBzxCyv0YoxN3LMrKB9zuhp1bV+LgewxuVwHdl4Gz5oePoUFrr9EfgJlGlDeXCU1b/yhdnXeuRvAnz8HN8Q==", - "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", - "license": "MIT" - }, - "node_modules/@swc/helpers": { - "version": "0.5.17", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", - "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "license": "MIT", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "license": "MIT" - }, - "node_modules/@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/fontkit": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@types/fontkit/-/fontkit-2.0.8.tgz", - "integrity": "sha512-wN+8bYxIpJf+5oZdrdtaX04qUuWHcKxcDEgRS9Qm9ZClSHjzEn13SxUC+5eRM+4yXIeTYk8mTzLAWGF64847ew==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/js-yaml": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", - "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", - "license": "MIT" - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/mdx": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", - "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", - "license": "MIT" - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" - }, - "node_modules/@types/nlcst": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", - "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/node": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", - "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", - "license": "MIT", - "dependencies": { - "undici-types": "~7.10.0" - } - }, - "node_modules/@types/sax": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", - "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "license": "ISC" - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "license": "ISC", - "dependencies": { - "string-width": "^4.1.0" - } - }, - "node_modules/ansi-align/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-align/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-regex": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", - "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/array-iterate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", - "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/astring": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", - "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", - "license": "MIT", - "bin": { - "astring": "bin/astring" - } - }, - "node_modules/astro": { - "version": "5.13.5", - "resolved": "https://registry.npmjs.org/astro/-/astro-5.13.5.tgz", - "integrity": "sha512-XmBzkl13XU97+n/QiOM5uXQdAVe0yKt5gO+Wlgc8dHRwHR499qhMQ5sMFckLJweUINLzcNGjP3F5nG4wV8a2XA==", - "license": "MIT", - "dependencies": { - "@astrojs/compiler": "^2.12.2", - "@astrojs/internal-helpers": "0.7.2", - "@astrojs/markdown-remark": "6.3.6", - "@astrojs/telemetry": "3.3.0", - "@capsizecss/unpack": "^2.4.0", - "@oslojs/encoding": "^1.1.0", - "@rollup/pluginutils": "^5.1.4", - "acorn": "^8.14.1", - "aria-query": "^5.3.2", - "axobject-query": "^4.1.0", - "boxen": "8.0.1", - "ci-info": "^4.2.0", - "clsx": "^2.1.1", - "common-ancestor-path": "^1.0.1", - "cookie": "^1.0.2", - "cssesc": "^3.0.0", - "debug": "^4.4.0", - "deterministic-object-hash": "^2.0.2", - "devalue": "^5.1.1", - "diff": "^5.2.0", - "dlv": "^1.1.3", - "dset": "^3.1.4", - "es-module-lexer": "^1.6.0", - "esbuild": "^0.25.0", - "estree-walker": "^3.0.3", - "flattie": "^1.1.1", - "fontace": "~0.3.0", - "github-slugger": "^2.0.0", - "html-escaper": "3.0.3", - "http-cache-semantics": "^4.1.1", - "import-meta-resolve": "^4.1.0", - "js-yaml": "^4.1.0", - "kleur": "^4.1.5", - "magic-string": "^0.30.17", - "magicast": "^0.3.5", - "mrmime": "^2.0.1", - "neotraverse": "^0.6.18", - "p-limit": "^6.2.0", - "p-queue": "^8.1.0", - "package-manager-detector": "^1.1.0", - "picomatch": "^4.0.2", - "prompts": "^2.4.2", - "rehype": "^13.0.2", - "semver": "^7.7.1", - "shiki": "^3.2.1", - "smol-toml": "^1.3.4", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.12", - "tsconfck": "^3.1.5", - "ultrahtml": "^1.6.0", - "unifont": "~0.5.0", - "unist-util-visit": "^5.0.0", - "unstorage": "^1.15.0", - "vfile": "^6.0.3", - "vite": "^6.3.4", - "vitefu": "^1.0.6", - "xxhash-wasm": "^1.1.0", - "yargs-parser": "^21.1.1", - "yocto-spinner": "^0.2.1", - "zod": "^3.24.4", - "zod-to-json-schema": "^3.24.5", - "zod-to-ts": "^1.2.0" - }, - "bin": { - "astro": "astro.js" - }, - "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/astrodotbuild" - }, - "optionalDependencies": { - "sharp": "^0.33.3" - } - }, - "node_modules/astro-expressive-code": { - "version": "0.41.3", - "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.41.3.tgz", - "integrity": "sha512-u+zHMqo/QNLE2eqYRCrK3+XMlKakv33Bzuz+56V1gs8H0y6TZ0hIi3VNbIxeTn51NLn+mJfUV/A0kMNfE4rANw==", - "license": "MIT", - "dependencies": { - "rehype-expressive-code": "^0.41.3" - }, - "peerDependencies": { - "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0" - } - }, - "node_modules/axobject-query": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", - "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/base-64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", - "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/bcp-47": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-2.1.0.tgz", - "integrity": "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w==", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/bcp-47-match": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz", - "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/blob-to-buffer": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/blob-to-buffer/-/blob-to-buffer-1.2.9.tgz", - "integrity": "sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "license": "ISC" - }, - "node_modules/boxen": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz", - "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", - "license": "MIT", - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^8.0.0", - "chalk": "^5.3.0", - "cli-boxes": "^3.0.0", - "string-width": "^7.2.0", - "type-fest": "^4.21.0", - "widest-line": "^5.0.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/brotli": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", - "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.1.2" - } - }, - "node_modules/camelcase": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", - "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chalk": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz", - "integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/ci-info": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", - "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-boxes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/collapse-white-space": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", - "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "license": "MIT", - "optional": true, - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT", - "optional": true - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", - "optional": true, - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/common-ancestor-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", - "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", - "license": "ISC" - }, - "node_modules/cookie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", - "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/cookie-es": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz", - "integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==", - "license": "MIT" - }, - "node_modules/cross-fetch": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", - "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", - "license": "MIT", - "dependencies": { - "node-fetch": "^2.7.0" - } - }, - "node_modules/crossws": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", - "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", - "license": "MIT", - "dependencies": { - "uncrypto": "^0.1.3" - } - }, - "node_modules/css-selector-parser": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.1.3.tgz", - "integrity": "sha512-gJMigczVZqYAk0hPVzx/M4Hm1D9QOtqkdQk9005TNzDIUGzo5cnHEDiKUT7jGPximL/oYb+LIitcHFQ4aKupxg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/mdevils" - }, - { - "type": "patreon", - "url": "https://patreon.com/mdevils" - } - ], - "license": "MIT" - }, - "node_modules/css-tree": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", - "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.12.2", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decode-named-character-reference": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", - "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", - "license": "MIT", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/defu": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", - "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", - "license": "MIT" - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/destr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", - "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/deterministic-object-hash": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/deterministic-object-hash/-/deterministic-object-hash-2.0.2.tgz", - "integrity": "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==", - "license": "MIT", - "dependencies": { - "base-64": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/devalue": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.3.2.tgz", - "integrity": "sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw==", - "license": "MIT" - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/dfa": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", - "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", - "license": "MIT" - }, - "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/direction": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz", - "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==", - "license": "MIT", - "bin": { - "direction": "cli.js" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT" - }, - "node_modules/dset": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", - "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/emoji-regex": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", - "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", - "license": "MIT" - }, - "node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "license": "MIT" - }, - "node_modules/esast-util-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", - "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/esast-util-from-js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", - "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "acorn": "^8.0.0", - "esast-util-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/esbuild": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", - "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.9", - "@esbuild/android-arm": "0.25.9", - "@esbuild/android-arm64": "0.25.9", - "@esbuild/android-x64": "0.25.9", - "@esbuild/darwin-arm64": "0.25.9", - "@esbuild/darwin-x64": "0.25.9", - "@esbuild/freebsd-arm64": "0.25.9", - "@esbuild/freebsd-x64": "0.25.9", - "@esbuild/linux-arm": "0.25.9", - "@esbuild/linux-arm64": "0.25.9", - "@esbuild/linux-ia32": "0.25.9", - "@esbuild/linux-loong64": "0.25.9", - "@esbuild/linux-mips64el": "0.25.9", - "@esbuild/linux-ppc64": "0.25.9", - "@esbuild/linux-riscv64": "0.25.9", - "@esbuild/linux-s390x": "0.25.9", - "@esbuild/linux-x64": "0.25.9", - "@esbuild/netbsd-arm64": "0.25.9", - "@esbuild/netbsd-x64": "0.25.9", - "@esbuild/openbsd-arm64": "0.25.9", - "@esbuild/openbsd-x64": "0.25.9", - "@esbuild/openharmony-arm64": "0.25.9", - "@esbuild/sunos-x64": "0.25.9", - "@esbuild/win32-arm64": "0.25.9", - "@esbuild/win32-ia32": "0.25.9", - "@esbuild/win32-x64": "0.25.9" - } - }, - "node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/estree-util-attach-comments": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", - "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-build-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", - "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-walker": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-scope": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", - "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-to-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", - "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "astring": "^1.8.0", - "source-map": "^0.7.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-visit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", - "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, - "node_modules/expressive-code": { - "version": "0.41.3", - "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.41.3.tgz", - "integrity": "sha512-YLnD62jfgBZYrXIPQcJ0a51Afv9h8VlWqEGK9uU2T5nL/5rb8SnA86+7+mgCZe5D34Tff5RNEA5hjNVJYHzrFg==", - "license": "MIT", - "dependencies": { - "@expressive-code/core": "^0.41.3", - "@expressive-code/plugin-frames": "^0.41.3", - "@expressive-code/plugin-shiki": "^0.41.3", - "@expressive-code/plugin-text-markers": "^0.41.3" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/flattie": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", - "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/fontace": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.3.0.tgz", - "integrity": "sha512-czoqATrcnxgWb/nAkfyIrRp6Q8biYj7nGnL6zfhTcX+JKKpWHFBnb8uNMw/kZr7u++3Y3wYSYoZgHkCcsuBpBg==", - "license": "MIT", - "dependencies": { - "@types/fontkit": "^2.0.8", - "fontkit": "^2.0.4" - } - }, - "node_modules/fontkit": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", - "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", - "license": "MIT", - "dependencies": { - "@swc/helpers": "^0.5.12", - "brotli": "^1.3.2", - "clone": "^2.1.2", - "dfa": "^1.2.0", - "fast-deep-equal": "^3.1.3", - "restructure": "^3.0.0", - "tiny-inflate": "^1.0.3", - "unicode-properties": "^1.4.0", - "unicode-trie": "^2.0.0" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.1.tgz", - "integrity": "sha512-R1QfovbPsKmosqTnPoRFiJ7CF9MLRgb53ChvMZm+r4p76/+8yKDy17qLL2PKInORy2RkZZekuK0efYgmzTkXyQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/github-slugger": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", - "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", - "license": "ISC" - }, - "node_modules/h3": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.4.tgz", - "integrity": "sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==", - "license": "MIT", - "dependencies": { - "cookie-es": "^1.2.2", - "crossws": "^0.3.5", - "defu": "^6.1.4", - "destr": "^2.0.5", - "iron-webcrypto": "^1.2.1", - "node-mock-http": "^1.0.2", - "radix3": "^1.1.2", - "ufo": "^1.6.1", - "uncrypto": "^0.1.3" - } - }, - "node_modules/hast-util-embedded": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz", - "integrity": "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-is-element": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-format": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hast-util-format/-/hast-util-format-1.1.0.tgz", - "integrity": "sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-embedded": "^3.0.0", - "hast-util-minify-whitespace": "^1.0.0", - "hast-util-phrasing": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "html-whitespace-sensitive-tag-names": "^3.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-html": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", - "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "devlop": "^1.1.0", - "hast-util-from-parse5": "^8.0.0", - "parse5": "^7.0.0", - "vfile": "^6.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", - "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "hastscript": "^9.0.0", - "property-information": "^7.0.0", - "vfile": "^6.0.0", - "vfile-location": "^5.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-has-property": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz", - "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-is-body-ok-link": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/hast-util-is-body-ok-link/-/hast-util-is-body-ok-link-3.0.1.tgz", - "integrity": "sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-is-element": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", - "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-minify-whitespace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hast-util-minify-whitespace/-/hast-util-minify-whitespace-1.0.1.tgz", - "integrity": "sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-embedded": "^3.0.0", - "hast-util-is-element": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-parse-selector": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-phrasing": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/hast-util-phrasing/-/hast-util-phrasing-3.0.1.tgz", - "integrity": "sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-embedded": "^3.0.0", - "hast-util-has-property": "^3.0.0", - "hast-util-is-body-ok-link": "^3.0.0", - "hast-util-is-element": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-raw": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", - "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "@ungap/structured-clone": "^1.0.0", - "hast-util-from-parse5": "^8.0.0", - "hast-util-to-parse5": "^8.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "parse5": "^7.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-select": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.4.tgz", - "integrity": "sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "bcp-47-match": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "css-selector-parser": "^3.0.0", - "devlop": "^1.0.0", - "direction": "^2.0.0", - "hast-util-has-property": "^3.0.0", - "hast-util-to-string": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "nth-check": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-estree": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", - "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-attach-comments": "^3.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-html": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", - "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-whitespace": "^3.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "stringify-entities": "^4.0.0", - "zwitch": "^2.0.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", - "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-parse5": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", - "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-parse5/node_modules/property-information": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", - "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-to-string": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz", - "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-text": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", - "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "hast-util-is-element": "^3.0.0", - "unist-util-find-after": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", - "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/html-escaper": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", - "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", - "license": "MIT" - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/html-whitespace-sensitive-tag-names": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-whitespace-sensitive-tag-names/-/html-whitespace-sensitive-tag-names-3.0.1.tgz", - "integrity": "sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "license": "BSD-2-Clause" - }, - "node_modules/i18next": { - "version": "23.16.8", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.8.tgz", - "integrity": "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==", - "funding": [ - { - "type": "individual", - "url": "https://locize.com" - }, - { - "type": "individual", - "url": "https://locize.com/i18next.html" - }, - { - "type": "individual", - "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" - } - ], - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.2" - } - }, - "node_modules/import-meta-resolve": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", - "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/inline-style-parser": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", - "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", - "license": "MIT" - }, - "node_modules/iron-webcrypto": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", - "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/brc-dd" - } - }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "license": "MIT", - "optional": true - }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "license": "MIT", - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-wsl": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", - "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", - "license": "MIT", - "dependencies": { - "is-inside-container": "^1.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/klona": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", - "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, - "node_modules/magic-string": { - "version": "0.30.18", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", - "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/magicast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" - } - }, - "node_modules/markdown-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", - "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/mdast-util-definitions": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz", - "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-directive": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.1.0.tgz", - "integrity": "sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", - "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", - "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", - "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", - "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", - "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", - "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdn-data": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", - "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", - "license": "CC0-1.0" - }, - "node_modules/micromark": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", - "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-directive": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", - "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "parse-entities": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "license": "MIT", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", - "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", - "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", - "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-expression": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", - "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-jsx": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", - "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-md": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", - "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", - "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", - "license": "MIT", - "dependencies": { - "acorn": "^8.0.0", - "acorn-jsx": "^5.0.0", - "micromark-extension-mdx-expression": "^3.0.0", - "micromark-extension-mdx-jsx": "^3.0.0", - "micromark-extension-mdx-md": "^2.0.0", - "micromark-extension-mdxjs-esm": "^3.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs-esm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", - "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-mdx-expression": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", - "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-events-to-acorn": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", - "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/mrmime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", - "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/neotraverse": { - "version": "0.6.18", - "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz", - "integrity": "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/nlcst-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", - "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch-native": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", - "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", - "license": "MIT" - }, - "node_modules/node-mock-http": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.2.tgz", - "integrity": "sha512-zWaamgDUdo9SSLw47we78+zYw/bDr5gH8pH7oRRs8V3KmBtu8GLgGIbV2p/gRPd3LWpEOpjQj7X1FOU3VFMJ8g==", - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/ofetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.4.1.tgz", - "integrity": "sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==", - "license": "MIT", - "dependencies": { - "destr": "^2.0.3", - "node-fetch-native": "^1.6.4", - "ufo": "^1.5.4" - } - }, - "node_modules/ohash": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", - "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", - "license": "MIT" - }, - "node_modules/oniguruma-parser": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", - "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==", - "license": "MIT" - }, - "node_modules/oniguruma-to-es": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.3.tgz", - "integrity": "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==", - "license": "MIT", - "dependencies": { - "oniguruma-parser": "^0.12.1", - "regex": "^6.0.1", - "regex-recursion": "^6.0.2" - } - }, - "node_modules/p-limit": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.2.0.tgz", - "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-queue": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.0.tgz", - "integrity": "sha512-mxLDbbGIBEXTJL0zEx8JIylaj3xQ7Z/7eEVjcF9fJX4DBiH9oqe+oahYnlKKxm0Ci9TlWTyhSHgygxMxjIB2jw==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^5.0.1", - "p-timeout": "^6.1.2" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-timeout": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz", - "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-manager-detector": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz", - "integrity": "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==", - "license": "MIT" - }, - "node_modules/pagefind": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.4.0.tgz", - "integrity": "sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g==", - "license": "MIT", - "bin": { - "pagefind": "lib/runner/bin.cjs" - }, - "optionalDependencies": { - "@pagefind/darwin-arm64": "1.4.0", - "@pagefind/darwin-x64": "1.4.0", - "@pagefind/freebsd-x64": "1.4.0", - "@pagefind/linux-arm64": "1.4.0", - "@pagefind/linux-x64": "1.4.0", - "@pagefind/windows-x64": "1.4.0" - } - }, - "node_modules/pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", - "license": "MIT" - }, - "node_modules/parse-entities": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/parse-latin": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", - "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "@types/unist": "^3.0.0", - "nlcst-to-string": "^4.0.0", - "unist-util-modify-children": "^4.0.0", - "unist-util-visit-children": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/prismjs": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prompts/node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/radix3": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", - "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", - "license": "MIT" - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/recma-build-jsx": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", - "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-util-build-jsx": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/recma-jsx": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz", - "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==", - "license": "MIT", - "dependencies": { - "acorn-jsx": "^5.0.0", - "estree-util-to-js": "^2.0.0", - "recma-parse": "^1.0.0", - "recma-stringify": "^1.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/recma-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", - "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "esast-util-from-js": "^2.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/recma-stringify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", - "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-util-to-js": "^2.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz", - "integrity": "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==", - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-recursion": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", - "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-utilities": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", - "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", - "license": "MIT" - }, - "node_modules/rehype": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.2.tgz", - "integrity": "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "rehype-parse": "^9.0.0", - "rehype-stringify": "^10.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-expressive-code": { - "version": "0.41.3", - "resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.41.3.tgz", - "integrity": "sha512-8d9Py4c/V6I/Od2VIXFAdpiO2kc0SV2qTJsRAaqSIcM9aruW4ASLNe2kOEo1inXAAkIhpFzAHTc358HKbvpNUg==", - "license": "MIT", - "dependencies": { - "expressive-code": "^0.41.3" - } - }, - "node_modules/rehype-format": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/rehype-format/-/rehype-format-5.0.1.tgz", - "integrity": "sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-format": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-parse": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", - "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-from-html": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-raw": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", - "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-raw": "^9.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-recma": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", - "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "hast-util-to-estree": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-stringify": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", - "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-to-html": "^9.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-directive": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.1.tgz", - "integrity": "sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-directive": "^3.0.0", - "micromark-extension-directive": "^3.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", - "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-mdx": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz", - "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==", - "license": "MIT", - "dependencies": { - "mdast-util-mdx": "^3.0.0", - "micromark-extension-mdxjs": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", - "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-smartypants": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.2.tgz", - "integrity": "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==", - "license": "MIT", - "dependencies": { - "retext": "^9.0.0", - "retext-smartypants": "^6.0.0", - "unified": "^11.0.4", - "unist-util-visit": "^5.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/restructure": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", - "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==", - "license": "MIT" - }, - "node_modules/retext": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", - "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "retext-latin": "^4.0.0", - "retext-stringify": "^4.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-latin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", - "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "parse-latin": "^7.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-smartypants": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", - "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "nlcst-to-string": "^4.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-stringify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", - "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "nlcst-to-string": "^4.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rollup": { - "version": "4.50.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.0.tgz", - "integrity": "sha512-/Zl4D8zPifNmyGzJS+3kVoyXeDeT/GrsJM94sACNg9RtUE0hrHa1bNPtRSrfHTMH5HjRzce6K7rlTh3Khiw+pw==", - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.50.0", - "@rollup/rollup-android-arm64": "4.50.0", - "@rollup/rollup-darwin-arm64": "4.50.0", - "@rollup/rollup-darwin-x64": "4.50.0", - "@rollup/rollup-freebsd-arm64": "4.50.0", - "@rollup/rollup-freebsd-x64": "4.50.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.50.0", - "@rollup/rollup-linux-arm-musleabihf": "4.50.0", - "@rollup/rollup-linux-arm64-gnu": "4.50.0", - "@rollup/rollup-linux-arm64-musl": "4.50.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.50.0", - "@rollup/rollup-linux-ppc64-gnu": "4.50.0", - "@rollup/rollup-linux-riscv64-gnu": "4.50.0", - "@rollup/rollup-linux-riscv64-musl": "4.50.0", - "@rollup/rollup-linux-s390x-gnu": "4.50.0", - "@rollup/rollup-linux-x64-gnu": "4.50.0", - "@rollup/rollup-linux-x64-musl": "4.50.0", - "@rollup/rollup-openharmony-arm64": "4.50.0", - "@rollup/rollup-win32-arm64-msvc": "4.50.0", - "@rollup/rollup-win32-ia32-msvc": "4.50.0", - "@rollup/rollup-win32-x64-msvc": "4.50.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/sax": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", - "license": "ISC" - }, - "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sharp": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", - "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", - "hasInstallScript": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.3", - "semver": "^7.6.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.33.5", - "@img/sharp-darwin-x64": "0.33.5", - "@img/sharp-libvips-darwin-arm64": "1.0.4", - "@img/sharp-libvips-darwin-x64": "1.0.4", - "@img/sharp-libvips-linux-arm": "1.0.5", - "@img/sharp-libvips-linux-arm64": "1.0.4", - "@img/sharp-libvips-linux-s390x": "1.0.4", - "@img/sharp-libvips-linux-x64": "1.0.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", - "@img/sharp-libvips-linuxmusl-x64": "1.0.4", - "@img/sharp-linux-arm": "0.33.5", - "@img/sharp-linux-arm64": "0.33.5", - "@img/sharp-linux-s390x": "0.33.5", - "@img/sharp-linux-x64": "0.33.5", - "@img/sharp-linuxmusl-arm64": "0.33.5", - "@img/sharp-linuxmusl-x64": "0.33.5", - "@img/sharp-wasm32": "0.33.5", - "@img/sharp-win32-ia32": "0.33.5", - "@img/sharp-win32-x64": "0.33.5" - } - }, - "node_modules/shiki": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.12.2.tgz", - "integrity": "sha512-uIrKI+f9IPz1zDT+GMz+0RjzKJiijVr6WDWm9Pe3NNY6QigKCfifCEv9v9R2mDASKKjzjQ2QpFLcxaR3iHSnMA==", - "license": "MIT", - "dependencies": { - "@shikijs/core": "3.12.2", - "@shikijs/engine-javascript": "3.12.2", - "@shikijs/engine-oniguruma": "3.12.2", - "@shikijs/langs": "3.12.2", - "@shikijs/themes": "3.12.2", - "@shikijs/types": "3.12.2", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "license": "MIT", - "optional": true, - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "license": "MIT" - }, - "node_modules/sitemap": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-8.0.0.tgz", - "integrity": "sha512-+AbdxhM9kJsHtruUF39bwS/B0Fytw6Fr1o4ZAIAEqA6cke2xcoO2GleBw9Zw7nRzILVEgz7zBM5GiTJjie1G9A==", - "license": "MIT", - "dependencies": { - "@types/node": "^17.0.5", - "@types/sax": "^1.2.1", - "arg": "^5.0.0", - "sax": "^1.2.4" - }, - "bin": { - "sitemap": "dist/cli.js" - }, - "engines": { - "node": ">=14.0.0", - "npm": ">=6.0.0" - } - }, - "node_modules/sitemap/node_modules/@types/node": { - "version": "17.0.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", - "license": "MIT" - }, - "node_modules/smol-toml": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.4.2.tgz", - "integrity": "sha512-rInDH6lCNiEyn3+hH8KVGFdbjc099j47+OSgbMrfDYX1CmXLfdKd7qi6IfcWj2wFxvSVkuI46M+wPGYfEOEj6g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 18" - }, - "funding": { - "url": "https://github.com/sponsors/cyyynthia" - } - }, - "node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 12" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/stream-replace-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz", - "integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==", - "license": "MIT" - }, - "node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/style-to-js": { - "version": "1.1.17", - "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.17.tgz", - "integrity": "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==", - "license": "MIT", - "dependencies": { - "style-to-object": "1.0.9" - } - }, - "node_modules/style-to-object": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.9.tgz", - "integrity": "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==", - "license": "MIT", - "dependencies": { - "inline-style-parser": "0.2.4" - } - }, - "node_modules/tiny-inflate": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", - "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", - "license": "MIT", - "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/tsconfck": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", - "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", - "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", - "license": "Apache-2.0", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/ufo": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", - "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", - "license": "MIT" - }, - "node_modules/ultrahtml": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.6.0.tgz", - "integrity": "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==", - "license": "MIT" - }, - "node_modules/uncrypto": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", - "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", - "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", - "license": "MIT" - }, - "node_modules/unicode-properties": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", - "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.0", - "unicode-trie": "^2.0.0" - } - }, - "node_modules/unicode-trie": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", - "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", - "license": "MIT", - "dependencies": { - "pako": "^0.2.5", - "tiny-inflate": "^1.0.0" - } - }, - "node_modules/unified": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unifont": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.5.2.tgz", - "integrity": "sha512-LzR4WUqzH9ILFvjLAUU7dK3Lnou/qd5kD+IakBtBK4S15/+x2y9VX+DcWQv6s551R6W+vzwgVS6tFg3XggGBgg==", - "license": "MIT", - "dependencies": { - "css-tree": "^3.0.0", - "ofetch": "^1.4.1", - "ohash": "^2.0.0" - } - }, - "node_modules/unist-util-find-after": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", - "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-modify-children": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", - "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "array-iterate": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", - "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", - "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-children": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", - "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unstorage": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.0.tgz", - "integrity": "sha512-l9Z7lBiwtNp8ZmcoZ/dmPkFXFdtEdZtTZafCSnEIj3YvtkXeGAtL2rN8MQFy/0cs4eOLpuRJMp9ivdug7TCvww==", - "license": "MIT", - "dependencies": { - "anymatch": "^3.1.3", - "chokidar": "^4.0.3", - "destr": "^2.0.5", - "h3": "^1.15.4", - "lru-cache": "^10.4.3", - "node-fetch-native": "^1.6.7", - "ofetch": "^1.4.1", - "ufo": "^1.6.1" - }, - "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.0.3 || ^7.0.0", - "@deno/kv": ">=0.9.0", - "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", - "@planetscale/database": "^1.19.0", - "@upstash/redis": "^1.34.3", - "@vercel/blob": ">=0.27.1", - "@vercel/functions": "^2.2.12", - "@vercel/kv": "^1.0.1", - "aws4fetch": "^1.0.20", - "db0": ">=0.2.1", - "idb-keyval": "^6.2.1", - "ioredis": "^5.4.2", - "uploadthing": "^7.4.4" - }, - "peerDependenciesMeta": { - "@azure/app-configuration": { - "optional": true - }, - "@azure/cosmos": { - "optional": true - }, - "@azure/data-tables": { - "optional": true - }, - "@azure/identity": { - "optional": true - }, - "@azure/keyvault-secrets": { - "optional": true - }, - "@azure/storage-blob": { - "optional": true - }, - "@capacitor/preferences": { - "optional": true - }, - "@deno/kv": { - "optional": true - }, - "@netlify/blobs": { - "optional": true - }, - "@planetscale/database": { - "optional": true - }, - "@upstash/redis": { - "optional": true - }, - "@vercel/blob": { - "optional": true - }, - "@vercel/functions": { - "optional": true - }, - "@vercel/kv": { - "optional": true - }, - "aws4fetch": { - "optional": true - }, - "db0": { - "optional": true - }, - "idb-keyval": { - "optional": true - }, - "ioredis": { - "optional": true - }, - "uploadthing": { - "optional": true - } - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-location": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", - "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vite": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@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": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vitefu": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.1.tgz", - "integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==", - "license": "MIT", - "workspaces": [ - "tests/deps/*", - "tests/projects/*", - "tests/projects/workspace/packages/*" - ], - "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/web-namespaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", - "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which-pm-runs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", - "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/widest-line": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", - "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", - "license": "MIT", - "dependencies": { - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/xxhash-wasm": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", - "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", - "license": "MIT" - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", - "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yocto-spinner": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/yocto-spinner/-/yocto-spinner-0.2.3.tgz", - "integrity": "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==", - "license": "MIT", - "dependencies": { - "yoctocolors": "^2.1.1" - }, - "engines": { - "node": ">=18.19" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoctocolors": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", - "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-to-json-schema": { - "version": "3.24.6", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", - "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.24.1" - } - }, - "node_modules/zod-to-ts": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/zod-to-ts/-/zod-to-ts-1.2.0.tgz", - "integrity": "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==", - "peerDependencies": { - "typescript": "^4.9.4 || ^5.0.2", - "zod": "^3" - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -} diff --git a/docs/package.json b/docs/package.json deleted file mode 100644 index cb7c155..0000000 --- a/docs/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "flashvm-docs", - "private": true, - "type": "module", - "scripts": { - "dev": "astro dev", - "build": "astro build", - "preview": "astro preview" - }, - "dependencies": { - "astro": "^5.13.3", - "@astrojs/starlight": "^0.34.8", - "@astrojs/mdx": "^4.3.4" - }, - "engines": { "node": ">=18" } -} diff --git a/docs/src/content/config.ts b/docs/src/content/config.ts deleted file mode 100644 index e54e72f..0000000 --- a/docs/src/content/config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { defineCollection } from 'astro:content'; -import { docsLoader, i18nLoader } from '@astrojs/starlight/loaders'; -import { docsSchema, i18nSchema } from '@astrojs/starlight/schema'; - -export const collections = { - docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), - i18n: defineCollection({ loader: i18nLoader(), schema: i18nSchema() }), -}; diff --git a/docs/src/content/docs/api.mdx b/docs/src/content/docs/api.mdx deleted file mode 100644 index 68ef697..0000000 --- a/docs/src/content/docs/api.mdx +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: Python API ---- - -## flashvm.prepare_image() - -Idempotently imports the embedded OCI image into local containers-storage (prefers `skopeo copy`, falls back to buildah). Called automatically on first `run` when needed. - -## flashvm.run(code: str, *, expect: list[str] | None = None, env: dict[str, str] | None = None, timeout: int | None = None) -> dict - -Executes `code` in a microVM. Returns a dict with: - -- `exit_code`: int -- `stdout`: str -- `stderr`: str -- `image_used`: str (image reference used to boot) -- `artifacts`: list[Artifact] — present when `expect` is provided - -Where `Artifact` contains: -- `guest_path`: str (path inside VM) -- `host_path`: str (temporary path on host) -- `size_bytes`: int - -Arguments: -- `expect`: glob(s) relative to `/work/out` in the guest to collect after run. -- `env`: environment variables for the guest process. -- `timeout`: optional timeout for the execution. - -Raises exceptions on startup or transport errors (e.g., missing KVM). diff --git a/docs/src/content/docs/changelog.mdx b/docs/src/content/docs/changelog.mdx deleted file mode 100644 index 2e6c677..0000000 --- a/docs/src/content/docs/changelog.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Changelog ---- - -All notable changes to this project will be documented here. - -- 0.1.1 — Initial docs and API pages. diff --git a/docs/src/content/docs/contributing.mdx b/docs/src/content/docs/contributing.mdx deleted file mode 100644 index d811b8c..0000000 --- a/docs/src/content/docs/contributing.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: Contributing ---- - -- Discuss ideas in issues. -- Run tests with `pytest -q`. -- For Rust/Python integration, use `maturin develop`. -- Keep docs updated when changing CLI or API. diff --git a/docs/src/content/docs/examples.mdx b/docs/src/content/docs/examples.mdx deleted file mode 100644 index 28f4537..0000000 --- a/docs/src/content/docs/examples.mdx +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: Examples ---- - -### Hello World -```python -import flashvm as fvm -print(fvm.run("print('hello')")["stdout"]) -``` - -### Artifacts -```python -code = """ -with open('/work/out/nums.txt', 'w') as f: - f.write('\n'.join(str(i) for i in range(3))) -""" -res = fvm.run(code, expect=["out/*.txt"]) -``` diff --git a/docs/src/content/docs/guides/isolation.mdx b/docs/src/content/docs/guides/isolation.mdx deleted file mode 100644 index 985a52d..0000000 --- a/docs/src/content/docs/guides/isolation.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: Isolation & Security ---- - -Each run occurs inside a microVM launched by krunvm (libkrun/KVM), offering stronger isolation than namespaces-only containers. - -- Dedicated VM process per run. -- Minimal userspace image. -- Limited host surface via standard transports and build tools. - -Review krunvm/libkrun documentation for threat model and platform support. diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx deleted file mode 100644 index 14a2128..0000000 --- a/docs/src/content/docs/index.mdx +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: flashVM ---- - -Run short Python snippets in a microVM for strong isolation—without asking users to pull images or learn container tooling. On first run, flashVM imports an embedded OCI image into local containers-storage, and then boots a microVM via krunvm (libkrun). The result (stdout, exit code, and optional artifacts) is returned to your Python process. - -> Install: `pip install flashvm` - -## Why flashVM? - -- Real isolation: every run executes inside a tiny KVM-backed microVM (via libkrun). -- Zero image setup: a minimal Python OCI image ships inside the wheel and is auto-imported on first use. -- Friendly API: one call (`flashvm.run(...)`) returns stdout/stderr, exit code, and optional output files (artifacts). - -## Requirements - -- Linux host with KVM available (`/dev/kvm`). -- System tools installed on the host: - - krunvm – launch microVMs from OCI images (uses libkrun/buildah). - - buildah – rootless image operations and storage. - - skopeo (optional, recommended) – fast, policy-aware copy from `oci:` to `containers-storage:`. -- Python 3.8+. - -## Installation - -From PyPI: - -```bash -pip install flashvm -``` - -From source (editable dev mode): - -```bash -pip install maturin -maturin develop -``` - -Note: krunvm/buildah/skopeo are host tools installed via your OS package manager. - -## Quick start - -```python -import flashvm as fvm - -# Optional: import the embedded OCI image now (idempotent) -fvm.prepare_image() # First run does this automatically if you skip it. - -# Run a short snippet in a microVM -res = fvm.run("print('Hello from microVM!')") -print("exit:", res["exit_code"]) -print("stdout:", res["stdout"]) -print("stderr:", res["stderr"]) -print("image_used:", res["image_used"]) -``` - -### With artifacts - -```python -code = r""" -with open('/work/out/result.txt', 'w') as f: - f.write('ok\n') -print('done') -""" - -res = fvm.run(code, expect=["out/*.txt"]) # collect files matching glob(s) from /work/out -for a in res["artifacts"]: - print(a["guest_path"], a["size_bytes"]) -``` - -## Troubleshooting - -- “KVM not available” – ensure hardware virtualization is enabled in BIOS/UEFI and `/dev/kvm` exists (check group permissions). -- Image/transport errors – if `skopeo` isn’t installed, flashVM falls back to a `buildah`-based import of the embedded `oci:` layout into `containers-storage:`. -- Rootless storage – containers-storage is used by buildah/skopeo; verify images with `buildah images`. - -## Security / Isolation notes - -flashVM relies on krunvm (which uses libkrun) to run each execution inside a microVM. This provides stronger isolation than plain containers while keeping startup latency low. See the krunvm project for platform support and background. - diff --git a/docs/src/content/docs/install.mdx b/docs/src/content/docs/install.mdx deleted file mode 100644 index 4dca50a..0000000 --- a/docs/src/content/docs/install.mdx +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: Installation & Requirements ---- - -flashVM runs short Python snippets inside a KVM microVM (via krunvm/libkrun) and ships a minimal Python OCI image embedded in the wheel. - -## Requirements - -- Linux host with KVM enabled (check `/dev/kvm`). -- Host tools installed: - - krunvm — launches microVMs from OCI images (uses libkrun/buildah). - - buildah — rootless image operations and local containers-storage. - - skopeo (optional, recommended) — fast copy from `oci:` to `containers-storage:`. -- Python 3.8+. - -## Install from PyPI - -```bash -pip install flashvm -``` - -## Install from source (dev) - -```bash -pip install maturin -maturin develop -``` - -Note: krunvm/buildah/skopeo are installed via your Linux distro (not pip). diff --git a/docs/src/content/docs/internals.mdx b/docs/src/content/docs/internals.mdx deleted file mode 100644 index 6c10705..0000000 --- a/docs/src/content/docs/internals.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: Internals ---- - -- Embedded OCI layout shipped inside the wheel under `flashvm/data/oci`. -- First-run import into containers-storage using skopeo/buildah. -- VM boot via `krunvm` with appropriate volume and command wiring. -- Result collection: stdout/stderr/exit code; optional artifact globbing under `/work/out`. diff --git a/docs/src/content/docs/license.mdx b/docs/src/content/docs/license.mdx deleted file mode 100644 index ea804f0..0000000 --- a/docs/src/content/docs/license.mdx +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: License ---- - -MIT License. See repository for details. diff --git a/docs/src/content/docs/quickstart.mdx b/docs/src/content/docs/quickstart.mdx deleted file mode 100644 index c73bdc0..0000000 --- a/docs/src/content/docs/quickstart.mdx +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: Quickstart ---- - -This page shows the minimal steps to run code inside a microVM and optionally collect artifacts. - -```python -import flashvm as fvm - -# Prepare embedded image (optional; auto on first run) -fvm.prepare_image() - -res = fvm.run("print('Hello from microVM!')") -print(res["exit_code"], res["stdout"], res["stderr"]) -``` - -### Collect artifacts - -```python -code = r""" -with open('/work/out/result.txt', 'w') as f: - f.write('ok\n') -print('done') -""" - -res = fvm.run(code, expect=["out/*.txt"]) # glob(s) under /work/out -for a in res["artifacts"]: - print(a["guest_path"], a["size_bytes"]) -``` diff --git a/docs/src/content/docs/reference/host-tools.mdx b/docs/src/content/docs/reference/host-tools.mdx deleted file mode 100644 index f3f588c..0000000 --- a/docs/src/content/docs/reference/host-tools.mdx +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: Host Tooling (krunvm/buildah/skopeo) ---- - -- krunvm: CLI that boots microVMs from OCI images using libkrun/KVM. -- buildah: container build/management; provides rootless containers-storage. -- skopeo: copy/sign/inspect images; used for fast `oci:` → `containers-storage:` import. - -Install via your Linux distro. See upstream docs for details. diff --git a/docs/src/content/docs/reference/result.mdx b/docs/src/content/docs/reference/result.mdx deleted file mode 100644 index 46e35e6..0000000 --- a/docs/src/content/docs/reference/result.mdx +++ /dev/null @@ -1,19 +0,0 @@ ---- -title: Result Schema ---- - -Successful `flashvm.run(...)` returns a dict: - -```json -{ - "exit_code": 0, - "stdout": "...", - "stderr": "...", - "image_used": "containers-storage:localhost/flashvm:latest", - "artifacts": [ - { "guest_path": "out/result.txt", "host_path": "/tmp/tmpfile", "size_bytes": 3 } - ] -} -``` - -On failure, exceptions include stderr details and hints when available. diff --git a/docs/src/content/docs/reference/transports.mdx b/docs/src/content/docs/reference/transports.mdx deleted file mode 100644 index bd43c88..0000000 --- a/docs/src/content/docs/reference/transports.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: Transport Syntax (oci:/containers-storage:) ---- - -- `oci:PATH[:ref]` — OCI layout stored at PATH (used inside the wheel). -- `containers-storage:NAME[:tag]` — local image store used by buildah/krunvm. - -`skopeo copy oci:/path containers-storage:localhost/flashvm:latest` diff --git a/docs/src/content/docs/troubleshooting.mdx b/docs/src/content/docs/troubleshooting.mdx deleted file mode 100644 index cedf61b..0000000 --- a/docs/src/content/docs/troubleshooting.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: Troubleshooting ---- - -- KVM not available: enable virtualization in BIOS/UEFI and ensure `/dev/kvm` exists. Check user/group permissions to access it. -- Missing tools: install `krunvm`, `buildah`, and optionally `skopeo` via your distro. -- Image import errors: try `skopeo copy` manually or ensure containers-storage is accessible. Rootless users can verify with `buildah images`. -- Timeouts: increase `timeout` in `flashvm.run(...)` or inspect stderr for hints. diff --git a/docs/src/content/docs/usage/artifacts.mdx b/docs/src/content/docs/usage/artifacts.mdx deleted file mode 100644 index 2bf30fd..0000000 --- a/docs/src/content/docs/usage/artifacts.mdx +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: Artifacts ---- - -Collect output files produced by your code inside the microVM. - -- Files should be written under `/work/out` in the guest. -- Pass one or more globs via `expect=["out/*.txt", "out/reports/**"]`. -- Returned artifacts include the guest path, a temporary host path, and file size. - -```python -res = fvm.run(code, expect=["out/*.txt"]) -for a in res["artifacts"]: - print(a["guest_path"], a["host_path"], a["size_bytes"]) -``` diff --git a/docs/src/content/docs/usage/image.mdx b/docs/src/content/docs/usage/image.mdx deleted file mode 100644 index 5c57dc1..0000000 --- a/docs/src/content/docs/usage/image.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: Embedded Image & Storage ---- - -flashVM embeds a minimal Python OCI image as package data. - -On first use, the image is imported into local containers-storage: - -- Preferred path: `skopeo copy oci:/path/in/wheel containers-storage:localhost/flashvm`. -- Fallback: buildah-based import when `skopeo` is unavailable. - -Notes: -- Rootless mode uses your user’s containers-storage. Check with `buildah images`. -- Transports `oci:` and `containers-storage:` are standard; see containers-transports manpage. diff --git a/docs/src/env.d.ts b/docs/src/env.d.ts deleted file mode 100644 index 9bc5cb4..0000000 --- a/docs/src/env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// \ No newline at end of file diff --git a/docs/tsconfig.json b/docs/tsconfig.json deleted file mode 100644 index e94966b..0000000 --- a/docs/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "compilerOptions": { - "baseUrl": ".", - "types": ["astro/client"], - "allowJs": true - } -} diff --git a/examples/basic_example.py b/examples/basic_example.py deleted file mode 100644 index ba0b256..0000000 --- a/examples/basic_example.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python3 -""" -Basic example of using the flashvm module - -This script demonstrates how to use the library to run Python code -in isolated microVMs using libkrun. -""" - -import flashvm as vm - -def main(): - """Basic execution demo""" - print("=== flashvm demonstration ===\n") - - print("=== Basic Test ===") - - code = """ -import sys -print(f"Python version: {sys.version}") -print("Hello from flashvm!") -print("Calculating 2 + 2 =", 2 + 2) - -# Demonstrate some built-in modules -import math -print(f"Pi = {math.pi:.4f}") -print(f"Square root of 16 = {math.sqrt(16)}") - -import datetime -now = datetime.datetime.now() -print(f"Current datetime: {now.strftime('%Y-%m-%d %H:%M:%S')}") -""" - - try: - result = vm.run(code) - if result['exit_code'] == 0: - print("✅ Execution succeeded!") - else: - print("❌ Execution failed") - print("Stdout:\n") - print(result['stdout']) - if result.get('stderr'): - print("Stderr:\n") - print(result['stderr']) - print(f"Exit code: {result['exit_code']}") - print(f"Execution time: {result['execution_time_ms']} ms") - print(f"Image used: {result.get('image_used', '')}") - except Exception as e: - print(f"❌ Error: {e}") - -if __name__ == "__main__": - main() diff --git a/flake.lock b/flake.lock index 2b1bdda..40e2620 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1756787288, - "narHash": "sha256-rw/PHa1cqiePdBxhF66V7R+WAP8WekQ0mCDG4CFqT8Y=", + "lastModified": 1757745802, + "narHash": "sha256-hLEO2TPj55KcUFUU1vgtHE9UEIOjRcH/4QbmfHNF820=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d0fc30899600b9b3466ddb260fd83deb486c32f1", + "rev": "c23193b943c6c689d70ee98ce3128239ed9e32d1", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index a44d17d..0f2c53c 100644 --- a/flake.nix +++ b/flake.nix @@ -1,212 +1,28 @@ { - description = "flashVM – reproducible OCI embed + PyPI publishing via Nix"; + description = "flashVM"; inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; outputs = { self, nixpkgs }: let - linuxSystems = [ "x86_64-linux" "aarch64-linux" ]; - forAll = nixpkgs.lib.genAttrs linuxSystems; + systems = [ "x86_64-linux" "aarch64-linux" ]; + forAll = nixpkgs.lib.genAttrs systems; in { - formatter = forAll (system: (import nixpkgs { inherit system; }).nixfmt); - - packages = forAll (system: - let - pkgs = import nixpkgs { inherit system; }; - - dockerImage = pkgs.dockerTools.buildImage { - name = "python-basic"; - tag = "latest"; - - copyToRoot = pkgs.buildEnv { - name = "python-basic-rootfs"; - paths = [ pkgs.coreutils pkgs.bash pkgs.python312Full ]; - }; - - config = { - Cmd = [ "python3" "-u" ]; - WorkingDir = "/"; - }; - }; - - ociLayout = pkgs.stdenvNoCC.mkDerivation { - pname = "flashvm-oci-layout"; - version = "0.1.0"; - nativeBuildInputs = [ pkgs.skopeo ]; - dontUnpack = true; - installPhase = '' - mkdir -p $out/oci - # tmp/runtime para skopeo funcionar no sandbox - TMP=${TMPDIR:-$PWD/tmp} - mkdir -p "$TMP" - export TMPDIR="$TMP" - export XDG_RUNTIME_DIR="$TMP" - # docker-archive: -> oci::tag - skopeo --tmpdir "$TMP" copy --insecure-policy docker-archive:${dockerImage} oci:$out/oci:python-basic - ''; - }; - in { - default = ociLayout; - oci-layout = ociLayout; - } - ); - devShells = forAll (system: let pkgs = import nixpkgs { inherit system; }; in { default = pkgs.mkShell { packages = [ - pkgs.python312 - pkgs.uv - pkgs.maturin - pkgs.rustc - pkgs.cargo - pkgs.pkg-config - pkgs.skopeo - pkgs.buildah - pkgs.podman + pkgs.python312 pkgs.maturin pkgs.uv + pkgs.rustc pkgs.cargo pkgs.pkg-config + pkgs.docker pkgs.docker-buildx pkgs.gnutar pkgs.coreutils pkgs.jq + pkgs.umoci pkgs.skopeo + pkgs.erofs-utils pkgs.squashfsTools pkgs.zstd pkgs.lz4 + pkgs.busybox pkgs.cpio pkgs.e2fsprogs pkgs.cloud-hypervisor ]; - shellHook = '' - echo ">> flashVM devshell — Python: $(python3 -V)" - echo "Use: nix run .#vendor-oci && nix run .#develop (in-tree) ou nix run .#build (wheels)" - ''; + shellHook = ''echo ">> flashVM dev shell ativa"''; }; } ); - - apps = forAll (system: - let - pkgs = import nixpkgs { inherit system; }; - oci = self.packages.${system}.oci-layout; - - vendorScript = pkgs.writeShellScript "vendor-oci" '' - set -euo pipefail - dst="flashvm/data/oci" - if [ -e "$dst" ]; then - chmod -R u+w "$dst" 2>/dev/null || true - rm -rf "$dst" - fi - mkdir -p "$dst" - cp -R "${oci}/oci/." "$dst/" - echo "✅ Vendored OCI layout into $dst" - ''; - - vendorDockerScript = pkgs.writeShellScript "vendor-oci-from-dockerfile" '' - set -euo pipefail - dst="flashvm/data/oci" - if [ -e "$dst" ]; then - chmod -R u+w "$dst" 2>/dev/null || true - rm -rf "$dst" - fi - mkdir -p "$dst" - img="localhost/flashvm-python-basic:latest" - ${pkgs.buildah}/bin/buildah bud -t "$img" -f docker/Dockerfile.python-basic docker - ${pkgs.skopeo}/bin/skopeo --insecure-policy copy containers-storage:"$img" oci:"$dst":python-basic - echo "✅ Vendored OCI layout from docker/Dockerfile.python-basic into $dst" - ''; - - buildScript = pkgs.writeShellScript "build-flashvm" '' - set -euo pipefail - ${pkgs.maturin}/bin/maturin build - wheel=$(ls -1t target/wheels/flashvm-*.whl | head -n1) - if [ -z "${wheel:-}" ]; then - echo "Could not find built wheel in target/wheels"; exit 1; - fi - echo "Extracting native extension from $wheel into flashvm/" - WHEEL="$wheel" python3 - <<'PY' -import os, zipfile, pathlib, sys -wheel = os.environ['WHEEL'] -zf = zipfile.ZipFile(wheel) -members = [m for m in zf.namelist() if m.startswith('flashvm/') and (m.endswith('.so') or m.endswith('.pyd'))] -if not members: - print('No native extension found inside wheel') - sys.exit(1) -target = pathlib.Path('flashvm') -target.mkdir(exist_ok=True) -for m in members: - name = m.split('/')[-1] - with zf.open(m) as src, open(target / name, 'wb') as dst: - dst.write(src.read()) -print(f"Extracted: {', '.join(members)}") -PY - ''; - - developScript = pkgs.writeShellScript "develop-flashvm" '' - set -euo pipefail - ${pkgs.maturin}/bin/maturin develop - if ls flashvm/_core* 1>/dev/null 2>&1; then - echo "✅ Found flashvm/_core*.so in repo; in-tree imports will work" - exit 0 - fi - py="$(command -v python3)" - so="$($py - <<'PY' -import sysconfig, pathlib -paths = [] -for key in ("platlib","purelib"): - p = sysconfig.get_paths().get(key) - if p: - paths.append(p) -found = None -for base in paths: - p = pathlib.Path(base)/"flashvm" - for ext in (".so", ".pyd"): - matches = list(p.glob(f"_core*{ext}")) - if matches: - found = matches[0] - break - if found: - break -print(found if found else "") -PY -)" - if [ -n "$so" ]; then - mkdir -p flashvm - ln -sf "$so" "flashvm/$(basename "$so")" - echo "✅ Linked $(basename "$so") into flashvm/ for in-tree imports" - exit 0 - fi - echo "Did not locate _core in site-packages; building a wheel to extract" - ${pkgs.maturin}/bin/maturin build - wheel=$(ls -1t target/wheels/flashvm-*.whl | head -n1) - if [ -z "${wheel:-}" ]; then - echo "Could not find built wheel in target/wheels"; exit 1 - fi - WHEEL="$wheel" python3 - <<'PY' -import os, zipfile, pathlib -wheel = os.environ['WHEEL'] -zf = zipfile.ZipFile(wheel) -members = [m for m in zf.namelist() if m.startswith('flashvm/') and (m.endswith('.so') or m.endswith('.pyd'))] -if not members: - raise SystemExit('No native extension found inside wheel') -target = pathlib.Path('flashvm') -target.mkdir(exist_ok=True) -for m in members: - name = m.split('/')[-1] - with zf.open(m) as src, open(target / name, 'wb') as dst: - dst.write(src.read()) -print(f"✅ Extracted {', '.join(members)} into flashvm/") -PY - ''; - - publishScript = pkgs.writeShellScript "publish-flashvm" '' - set -euo pipefail - test -n "''${UV_PUBLISH_TOKEN-}" || { echo "UV_PUBLISH_TOKEN not set"; exit 1; } - # Build only wheels to avoid sdist (git HEAD) issues - rm -rf dist - ${pkgs.maturin}/bin/maturin build --release -o dist - if ! ls dist/*.whl 1>/dev/null 2>&1; then - echo "No wheels found in dist/"; exit 1; - fi - # Publish wheel(s) - ${pkgs.uv}/bin/uv publish --token "$UV_PUBLISH_TOKEN" dist/*.whl - ''; - in { - vendor-oci = { type = "app"; program = "${vendorScript}"; }; - vendor-oci-docker = { type = "app"; program = "${vendorDockerScript}"; }; - build = { type = "app"; program = "${buildScript}"; }; - develop = { type = "app"; program = "${developScript}"; }; - publish = { type = "app"; program = "${publishScript}"; }; - } - ); }; } diff --git a/flashvm/__init__.py b/flashvm/__init__.py deleted file mode 100644 index 9ca606c..0000000 --- a/flashvm/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from ._core import * # noqa: F403 - -__all__ = [name for name in dir() if not name.startswith("_")] -__version__ = "0.1.1" diff --git a/flashvm/data/oci/blobs/sha256/39dbf954503055210a0ccbee25fdb599d9d3988b45da3978d7025c623db11909 b/flashvm/data/oci/blobs/sha256/39dbf954503055210a0ccbee25fdb599d9d3988b45da3978d7025c623db11909 deleted file mode 100644 index 4959c0a..0000000 --- a/flashvm/data/oci/blobs/sha256/39dbf954503055210a0ccbee25fdb599d9d3988b45da3978d7025c623db11909 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:39dbf954503055210a0ccbee25fdb599d9d3988b45da3978d7025c623db11909 -size 1327080 diff --git a/flashvm/data/oci/blobs/sha256/89b2a69cdaa732c456834d53d9dc3e25bbeba1744fc3a79ba52493c28ed21d56 b/flashvm/data/oci/blobs/sha256/89b2a69cdaa732c456834d53d9dc3e25bbeba1744fc3a79ba52493c28ed21d56 deleted file mode 100644 index 540c592..0000000 --- a/flashvm/data/oci/blobs/sha256/89b2a69cdaa732c456834d53d9dc3e25bbeba1744fc3a79ba52493c28ed21d56 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:89b2a69cdaa732c456834d53d9dc3e25bbeba1744fc3a79ba52493c28ed21d56 -size 268 diff --git a/flashvm/data/oci/blobs/sha256/ca9e0bf09fc325818b461d3aa6975cf84ae2b4b9258d6cda7a2a7d39971f30f5 b/flashvm/data/oci/blobs/sha256/ca9e0bf09fc325818b461d3aa6975cf84ae2b4b9258d6cda7a2a7d39971f30f5 deleted file mode 100644 index 30b609d..0000000 --- a/flashvm/data/oci/blobs/sha256/ca9e0bf09fc325818b461d3aa6975cf84ae2b4b9258d6cda7a2a7d39971f30f5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ca9e0bf09fc325818b461d3aa6975cf84ae2b4b9258d6cda7a2a7d39971f30f5 -size 12550707 diff --git a/flashvm/data/oci/blobs/sha256/e4ba4d6f4188ade48f670414d944a9cd4454508c0adfdac240e9e81092fa831c b/flashvm/data/oci/blobs/sha256/e4ba4d6f4188ade48f670414d944a9cd4454508c0adfdac240e9e81092fa831c deleted file mode 100644 index 269c8ea..0000000 --- a/flashvm/data/oci/blobs/sha256/e4ba4d6f4188ade48f670414d944a9cd4454508c0adfdac240e9e81092fa831c +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e4ba4d6f4188ade48f670414d944a9cd4454508c0adfdac240e9e81092fa831c -size 201067936 diff --git a/flashvm/data/oci/blobs/sha256/f34d2f7d34ea07609ad343af1c7dbebad6d22895fb3e68d4834fc18286de2d2a b/flashvm/data/oci/blobs/sha256/f34d2f7d34ea07609ad343af1c7dbebad6d22895fb3e68d4834fc18286de2d2a deleted file mode 100644 index 33523ff..0000000 --- a/flashvm/data/oci/blobs/sha256/f34d2f7d34ea07609ad343af1c7dbebad6d22895fb3e68d4834fc18286de2d2a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f34d2f7d34ea07609ad343af1c7dbebad6d22895fb3e68d4834fc18286de2d2a -size 7268 diff --git a/flashvm/data/oci/blobs/sha256/f716fb09bc521d3afbb665994a43a221bf31b672d81fa2140e8b80a1c6c9b227 b/flashvm/data/oci/blobs/sha256/f716fb09bc521d3afbb665994a43a221bf31b672d81fa2140e8b80a1c6c9b227 deleted file mode 100644 index 0506567..0000000 --- a/flashvm/data/oci/blobs/sha256/f716fb09bc521d3afbb665994a43a221bf31b672d81fa2140e8b80a1c6c9b227 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f716fb09bc521d3afbb665994a43a221bf31b672d81fa2140e8b80a1c6c9b227 -size 1700 diff --git a/flashvm/data/oci/blobs/sha256/fbc8c3c9054a9d4f998cb1fecbfd6437a517df8b0c26c1682054a102182a6dc5 b/flashvm/data/oci/blobs/sha256/fbc8c3c9054a9d4f998cb1fecbfd6437a517df8b0c26c1682054a102182a6dc5 deleted file mode 100644 index 09ad53e..0000000 --- a/flashvm/data/oci/blobs/sha256/fbc8c3c9054a9d4f998cb1fecbfd6437a517df8b0c26c1682054a102182a6dc5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fbc8c3c9054a9d4f998cb1fecbfd6437a517df8b0c26c1682054a102182a6dc5 -size 31825775 diff --git a/flashvm/data/oci/index.json b/flashvm/data/oci/index.json deleted file mode 100644 index 3d5b61e..0000000 --- a/flashvm/data/oci/index.json +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d53426cc629460ffb10ab41c91adee8c1a70a04c84baf3b31ea8777de08a7582 -size 254 diff --git a/flashvm/data/oci/oci-layout b/flashvm/data/oci/oci-layout deleted file mode 100644 index 5f88fd9..0000000 --- a/flashvm/data/oci/oci-layout +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:18f0797eab35a4597c1e9624aa4f15fd91f6254e5538c1e0d193b2a95dd4acc6 -size 30 diff --git a/pyproject.toml b/pyproject.toml index 1cd69bd..7abd064 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,111 +1,39 @@ [build-system] -requires = ["maturin>=1.0,<2.0"] +requires = ["maturin>=1.7,<2"] build-backend = "maturin" [project] name = "flashvm" -dynamic = ["version"] -description = "Run Python code inside libkrun microVMs, with an embedded offline OCI image" -authors = [{ name = "fullzer4", email = "fullzer4@example.com" }] -keywords = ["python", "vm", "isolation", "libkrun", "krunvm", "oci", "skopeo"] +version = "0.1.0" +description = "Python-first microVM runner (rust-vmm + PyO3) to run OCI-prepared Python code in isolated VMs" +readme = "README.md" +requires-python = ">=3.8" +license = { text = "Apache-2.0 OR BSD-3-Clause" } +authors = [{ name = "fullzer4", email = "gabrielpelizzaro@gmail.com" }] +keywords = ["rust", "python", "pyo3", "kvm", "vm", "rust-vmm", "microvm"] classifiers = [ - "Development Status :: 3 - Alpha", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Rust", -] -requires-python = ">=3.8" -license = { text = "MIT" } -readme = "README.md" - -[project.urls] -Repository = "https://github.com/fullzer4/flashvm" - -[project.optional-dependencies] -dev = [ - "pytest>=7.0", - "pytest-cov>=4.0", - "pytest-timeout>=2.1", - "pytest-xdist>=3.0", - "pytest-benchmark>=4.0", - "pytest-asyncio>=0.21", - "pytest-mock>=3.10", - "hypothesis>=6.75", - "maturin>=1.0,<2.0", -] -test = [ - "pytest>=7.0", - "pytest-cov>=4.0", - "pytest-timeout>=2.1", + "Operating System :: POSIX :: Linux", + "License :: OSI Approved :: Apache Software License", + "License :: OSI Approved :: BSD License", ] [tool.maturin] -bindings = "pyo3" -python-source = "." -module-name = "flashvm._core" -include = [ - { path = "flashvm/data/**", format = "wheel" }, - { path = "flashvm/data/**", format = "sdist" } -] - -[tool.pytest.ini_options] -minversion = "7.0" -addopts = [ - "-ra", - "--strict-markers", - "--strict-config", - "--cov=flashvm", - "--cov-report=term-missing", - "--cov-report=html:reports/coverage/html", - "--cov-report=xml:reports/coverage/coverage.xml", - "--cov-report=json:reports/coverage/coverage.json", - "--dist=worksteal", -] -testpaths = ["tests"] -markers = [ - "slow: marks tests as slow (deselect with '-m \"not slow\"')", - "integration: marks tests as integration tests", - "unit: marks tests as unit tests", - "benchmark: marks tests as benchmark tests", - "requires_vm: marks tests that require VM capabilities", -] -timeout = 300 -filterwarnings = ["error", "ignore::UserWarning", "ignore::DeprecationWarning"] - -[tool.coverage.run] -source = ["flashvm"] -data_file = "reports/coverage/.coverage" -omit = ["*/tests/*", "*/test_*"] +python-source = "python" +python-packages = ["flashvm"] -[tool.coverage.html] -directory = "reports/coverage/html" - -[tool.coverage.xml] -output = "reports/coverage/coverage.xml" - -[tool.coverage.json] -output = "reports/coverage/coverage.json" +bindings = "pyo3" +compatibility = "manylinux_2_28" +strip = true -[tool.coverage.report] -exclude_lines = [ - "pragma: no cover", - "def __repr__", - "if self.debug:", - "if settings.DEBUG", - "raise AssertionError", - "raise NotImplementedError", - "if 0:", - "if __name__ == .__main__.:", - "class .*\\bProtocol\\):", - "@(abc\\.)?abstractmethod", +include = [ + { path = "README.md", format = ["sdist", "wheel"] }, + { path = "LICENSE*", format = ["sdist", "wheel"] }, + { path = "python/flashvm/assets/**", format = ["sdist", "wheel"] } ] -[dependency-groups] -dev = ["pytest-xdist>=3.6.1"] +[project.urls] +Homepage = "https://example.com/flashvm" +Source = "https://example.com/flashvm/repo" diff --git a/python/flashvm/__init__.py b/python/flashvm/__init__.py new file mode 100644 index 0000000..3f15a18 --- /dev/null +++ b/python/flashvm/__init__.py @@ -0,0 +1,4 @@ +from .flashvm_native import prepare_image, run # type: ignore + + +__all__ = ["prepare_image", "run"] \ No newline at end of file diff --git a/src/config.rs b/src/config.rs deleted file mode 100644 index bde08a2..0000000 --- a/src/config.rs +++ /dev/null @@ -1,108 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::path::PathBuf; -use std::time::Duration; - -/// Main configuration for running Python code in a microVM -#[derive(Debug, Clone)] -pub struct VMConfig { - /// Image reference (None = use embedded image; Some = docker://, containers-storage:, simple name, oci:, dir:, oci-archive:) - pub image: Option, - /// CPUs - pub cpus: u32, - /// Memory (MB) - pub memory_mb: u32, - /// Environment variables - pub env: HashMap, - /// Working directory in the guest (top-level, e.g., /work) - pub workdir: String, - /// Overall timeout - pub timeout: Duration, - /// Enable network - pub network: bool, - /// Ports (host:guest) when network=true - pub ports: Vec<(u16, u16)>, - /// Extra Python args (e.g., -u) - pub python_args: Vec, - /// Max size in bytes to inline artifacts - pub max_bytes_inline: u64, -} - -impl Default for VMConfig { - fn default() -> Self { - Self { - image: None, - cpus: 1, - memory_mb: 512, - env: HashMap::new(), - workdir: "/work".to_string(), - timeout: Duration::from_secs(30), - network: false, - ports: vec![], - python_args: vec!["-u".to_string()], - max_bytes_inline: 1024 * 1024, // 1MB - } - } -} - -/// Input files -#[derive(Debug, Clone)] -pub struct FileInput { - pub host_path: PathBuf, - pub guest_path: String, // relative to /work/in/ -} - -/// Output patterns (glob under /work/out) -#[derive(Debug, Clone)] -pub struct FileOutput { - pub pattern: String, // e.g.: "out/*.parquet" -} - -/// Execution result -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ExecutionResult { - pub stdout: String, - pub stderr: String, - pub exit_code: i32, - pub execution_time: Duration, - pub artifacts: Vec, - pub image_used: String, -} - -/// Collected artifact -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Artifact { - pub guest_path: String, - pub host_path: PathBuf, - pub size_bytes: u64, - pub content: Option>, -} - -/// Capture mode (future use) -#[derive(Debug, Clone)] -pub enum CaptureMode { - Stdio, - JsonLines, - Both, -} - -/// Local cache/config -#[derive(Debug, Clone)] -pub struct CacheConfig { - pub cache_dir: String, - pub cache_ttl_seconds: u64, - pub max_cache_size_mb: u64, -} - -impl Default for CacheConfig { - fn default() -> Self { - Self { - cache_dir: format!( - "{}/.cache/flashvm", - std::env::var("HOME").unwrap_or("/tmp".to_string()) - ), - cache_ttl_seconds: 24 * 3600, - max_cache_size_mb: 1024, - } - } -} diff --git a/src/error.rs b/src/error.rs index 7edd208..de49887 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,39 +1,5 @@ use pyo3::exceptions::PyRuntimeError; -use std::fmt; +use pyo3::PyErr; -#[derive(Debug)] -pub enum VMError { - ImageResolution(String), - VMConfiguration(String), - Execution(String), - IO(std::io::Error), - Timeout(String), - MissingDependency(String), - Cache(String), -} -impl fmt::Display for VMError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - VMError::ImageResolution(msg) => write!(f, "Image resolution error: {}", msg), - VMError::VMConfiguration(msg) => write!(f, "VM configuration error: {}", msg), - VMError::Execution(msg) => write!(f, "Execution error: {}", msg), - VMError::IO(err) => write!(f, "I/O error: {}", err), - VMError::Timeout(msg) => write!(f, "Timeout: {}", msg), - VMError::MissingDependency(dep) => write!(f, "Missing dependency: {}", dep), - VMError::Cache(msg) => write!(f, "Cache error: {}", msg), - } - } -} - -impl std::error::Error for VMError {} - -impl From for VMError { - fn from(err: std::io::Error) -> Self { VMError::IO(err) } -} - -impl From for VMError { - fn from(err: anyhow::Error) -> Self { VMError::Execution(err.to_string()) } -} - -pub type PyVMError = PyRuntimeError; +pub fn pyerr(e: E) -> PyErr { PyRuntimeError::new_err(e.to_string()) } \ No newline at end of file diff --git a/src/image_resolver.rs b/src/image_resolver.rs deleted file mode 100644 index 5ac7e95..0000000 --- a/src/image_resolver.rs +++ /dev/null @@ -1,286 +0,0 @@ -use crate::config::CacheConfig; -use crate::error::VMError; -use crate::wheel_resources::WheelResources; -use anyhow::Result; -use log::{debug, info, warn}; -use pyo3::Python; -use std::fs; -use std::io::Write; -use std::path::{Path, PathBuf}; -use std::process::Command; - -pub struct ImageResolver { - cache_config: CacheConfig, -} - -const CANONICAL_IMAGE: &str = "localhost/flashvm:python-basic"; -const EMBEDDED_TAG: &str = "python-basic"; - -impl ImageResolver { - pub fn new() -> Self { Self { cache_config: CacheConfig::default() } } - - /// Resolve image reference: - /// - None => import (once) the embedded OCI layout → containers-storage: and return canonical name - /// - Some => validate/normalize (accepts docker://, containers-storage:, simple name, oci:/dir..., dir:, oci-archive:) - pub fn resolve_image_ref(&self, image_ref: Option<&str>) -> Result { - match image_ref { - None => { - self.ensure_embedded_image_imported()?; - Ok(CANONICAL_IMAGE.to_string()) - } - Some(s) => self.validate_image_ref(s), - } - } - - /// Import the embedded OCI layout into containers-storage (idempotent). - fn ensure_embedded_image_imported(&self) -> Result<(), VMError> { - if self.image_exists_in_storage(CANONICAL_IMAGE)? { - debug!("Image already present in containers-storage: {}", CANONICAL_IMAGE); - return Ok(()); - } - - let oci_path: PathBuf = Python::with_gil(|py| { - WheelResources::find_embedded_data_path(py).map_err(|e| { - VMError::ImageResolution(format!("Failed to locate embedded data: {}", e)) - }) - })? - .ok_or_else(|| { - VMError::ImageResolution( - "Embedded OCI image not found (flashvm/data/oci)".to_string(), - ) - })?; - - self.validate_oci_layout_dir(&oci_path)?; - - let source_oci = format!("oci:{}:{}", oci_path.to_string_lossy(), EMBEDDED_TAG); - - if self.command_exists("skopeo") { - info!( - "Importing embedded image with skopeo: {} -> containers-storage:{}", - source_oci, CANONICAL_IMAGE - ); - let copy_cmd = format!( - "skopeo copy --insecure-policy '{}' 'containers-storage:{}'", - source_oci, CANONICAL_IMAGE - ); - if self.run_in_buildah_unshare(©_cmd)? { - self.mark_import_sentinel(&oci_path)?; - return Ok(()); - } else { - warn!("skopeo copy failed; trying fallback with buildah"); - } - } - - info!("Importing via buildah (fallback) from {}", source_oci); - let from_out = self.run_in_buildah_unshare_capture(&format!("buildah from '{}'", source_oci))?; - if !from_out.success { - return Err(VMError::ImageResolution(format!( - "buildah from failed: {}", - from_out.stderr - ))); - } - let container_name = from_out.stdout.trim(); - if container_name.is_empty() { - return Err(VMError::ImageResolution( - "buildah from did not return a container name".to_string(), - )); - } - let ok_commit = - self.run_in_buildah_unshare(&format!("buildah commit '{}' '{}'", container_name, CANONICAL_IMAGE))?; - let _ = self.run_in_buildah_unshare(&format!("buildah rm '{}'", container_name)); - if !ok_commit { - return Err(VMError::ImageResolution( - "buildah commit failed in fallback".to_string(), - )); - } - self.mark_import_sentinel(&oci_path)?; - Ok(()) - } - - fn mark_import_sentinel(&self, oci_path: &Path) -> Result<(), VMError> { - let cache_dir = PathBuf::from(&self.cache_config.cache_dir).join("state"); - fs::create_dir_all(&cache_dir)?; - let sentinel_path = cache_dir.join("embedded_import.json"); - let content = format!( - "{{\n \"image\": \"{}\",\n \"oci_path\": \"{}\",\n \"version\": \"{}\"\n}}\n", - CANONICAL_IMAGE, - oci_path.to_string_lossy(), - env!("CARGO_PKG_VERSION") - ); - let mut f = fs::File::create(&sentinel_path)?; - f.write_all(content.as_bytes())?; - Ok(()) - } - - fn image_exists_in_storage(&self, name: &str) -> Result { - let out = self.run_in_buildah_unshare_capture("buildah images --format '{{.Name}}:{{.Tag}}'")?; - if !out.success { - return Err(VMError::Execution(format!( - "Failed to list images in containers-storage: {}", - out.stderr - ))); - } - Ok(out.stdout.lines().any(|l| l.trim() == name)) - } - - fn run_in_buildah_unshare(&self, sh_cmd: &str) -> Result { - debug!("Executing: buildah unshare sh -c '{}'", sh_cmd); - let status = Command::new("buildah") - .args(["unshare", "sh", "-c", sh_cmd]) - .status() - .map_err(|e| VMError::Execution(format!("Failed to execute command: {}", e)))?; - Ok(status.success()) - } - - fn run_in_buildah_unshare_capture(&self, sh_cmd: &str) -> Result { - debug!("Executing: buildah unshare sh -c '{}'", sh_cmd); - let output = Command::new("buildah") - .args(["unshare", "sh", "-c", sh_cmd]) - .output() - .map_err(|e| VMError::Execution(format!("Failed to execute command: {}", e)))?; - Ok(Captured { - stdout: String::from_utf8_lossy(&output.stdout).to_string(), - stderr: String::from_utf8_lossy(&output.stderr).to_string(), - success: output.status.success(), - }) - } - - fn validate_oci_layout_dir(&self, oci_dir: &Path) -> Result<(), VMError> { - let layout_file = oci_dir.join("oci-layout"); - let index_file = oci_dir.join("index.json"); - let blobs_sha256_dir = oci_dir.join("blobs").join("sha256"); - if !layout_file.exists() || !index_file.exists() || !blobs_sha256_dir.exists() { - return Err(VMError::ImageResolution(format!( - "Invalid OCI layout in {}: requires oci-layout, index.json and blobs/sha256", - oci_dir.to_string_lossy() - ))); - } - Ok(()) - } - - fn validate_image_ref(&self, image_ref: &str) -> Result { - info!("Validating image reference: {}", image_ref); - - if image_ref.starts_with("docker://") { - self.validate_docker_ref(image_ref) - } else if image_ref.starts_with("containers-storage:") { - debug!("containers-storage accepted: {}", image_ref); - Ok(image_ref.to_string()) - } else if image_ref.starts_with("oci:") - || image_ref.starts_with("dir:") - || image_ref.starts_with("oci-archive:") - { - self.get_image_path_ref(image_ref) - } else { - debug!("Assuming simple name (docker): {}", image_ref); - Ok(image_ref.to_string()) - } - } - - fn get_image_path_ref(&self, image_ref: &str) -> Result { - if image_ref.starts_with("oci:") { - return self.validate_oci_ref(image_ref); - } - if image_ref.starts_with("docker://") { - return self.validate_docker_ref(image_ref); - } - - let path_ref = if image_ref.starts_with("dir:") { - &image_ref[4..] - } else if image_ref.starts_with("oci-archive:") { - &image_ref[12..] - } else { - image_ref - }; - - let path = Path::new(path_ref); - if path.exists() { - return Ok(image_ref.to_string()); - } - - Err(VMError::ImageResolution(format!( - "Could not resolve image reference: {}", - image_ref - ))) - } - - fn validate_oci_ref(&self, oci_ref: &str) -> Result { - // oci:[:tag] - let parts: Vec<&str> = oci_ref.splitn(2, ':').collect(); - if parts.len() < 2 { - return Err(VMError::ImageResolution( - "Invalid OCI reference. Expected: oci:[:tag]".to_string(), - )); - } - let path_part = parts[1]; - let (path, _tag) = if let Some(colon_pos) = path_part.rfind(':') { - (&path_part[..colon_pos], &path_part[colon_pos + 1..]) - } else { - (path_part, "latest") - }; - - let oci_path = Path::new(path); - if !oci_path.exists() { - return Err(VMError::ImageResolution(format!( - "OCI path does not exist: {}", - path - ))); - } - let layout_file = oci_path.join("oci-layout"); - let index_file = oci_path.join("index.json"); - let blobs_sha256 = oci_path.join("blobs").join("sha256"); - if !layout_file.exists() || !index_file.exists() || !blobs_sha256.exists() { - return Err(VMError::ImageResolution(format!( - "Invalid OCI layout in {}: requires oci-layout, index.json and blobs/sha256", - path - ))); - } - debug!("OCI reference validated: {}", oci_ref); - Ok(oci_ref.to_string()) - } - - fn validate_docker_ref(&self, docker_ref: &str) -> Result { - if !docker_ref.starts_with("docker://") { - return Err(VMError::ImageResolution( - "Docker reference must start with docker://".to_string(), - )); - } - let image_name = &docker_ref[9..]; - if image_name.is_empty() { - return Err(VMError::ImageResolution( - "Docker image name cannot be empty".to_string(), - )); - } - debug!("Docker reference validated ⇒ {}", image_name); - Ok(image_name.to_string()) - } - - pub fn list_cached_images(&self) -> Result, VMError> { - Ok(vec![]) - } - pub fn clear_cache(&self) -> Result<(), VMError> { Ok(()) } - - fn command_exists(&self, command: &str) -> bool { - Command::new("which") - .arg(command) - .output() - .map(|o| o.status.success()) - .unwrap_or(false) - } - - pub fn embedded_is_imported(&self) -> Result { - self.image_exists_in_storage(CANONICAL_IMAGE) - } - pub fn import_embedded_now(&self) -> Result<(), VMError> { - self.ensure_embedded_image_imported() - } -} - -impl Default for ImageResolver { fn default() -> Self { Self::new() } } - -#[derive(Debug)] -struct Captured { - stdout: String, - stderr: String, - success: bool, -} diff --git a/src/lib.rs b/src/lib.rs index c068d8b..6d5376a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,330 +1,39 @@ use pyo3::prelude::*; use pyo3::types::PyDict; -use std::collections::HashMap; -use std::time::Duration; -mod vm_runner; -mod image_resolver; -mod config; -mod error; -mod wheel_resources; - -use vm_runner::VMRunner; -use image_resolver::ImageResolver; -use config::{FileInput, FileOutput, VMConfig}; -use error::PyVMError; -use crate::error::VMError as InternalVMError; -use wheel_resources::find_embedded_data_path; - -#[pyfunction] -#[pyo3(signature = ( - code, - image = None, - cpus = None, - memory_mb = None, - env = None, - timeout_seconds = None, - workdir = None, - python_args = None, - network = None, - ports = None, - files_in = None, - expect = None, - max_bytes_inline = None, -))] -fn run( - py: Python, - code: String, - image: Option, - cpus: Option, - memory_mb: Option, - env: Option>, - timeout_seconds: Option, - workdir: Option, - python_args: Option>, - network: Option, - ports: Option>, - files_in: Option>, - expect: Option>, - max_bytes_inline: Option, -) -> PyResult { - let config = VMConfig { - image, - cpus: cpus.unwrap_or(1), - memory_mb: memory_mb.unwrap_or(512), - env: env.unwrap_or_default(), - workdir: workdir.unwrap_or_else(|| "/work".to_string()), - timeout: Duration::from_secs(timeout_seconds.unwrap_or(30)), - network: network.unwrap_or(false), - ports: ports.unwrap_or_default(), - python_args: python_args.unwrap_or_else(|| vec!["-u".to_string()]), - max_bytes_inline: max_bytes_inline.unwrap_or(1024 * 1024), - }; - - if !config.workdir.starts_with('/') || config.workdir.matches('/').count() > 1 { - return Err(PyVMError::new_err( - "workdir must be a top-level directory (e.g., /work)".to_string(), - )); - } - - let files_in_vec: Vec = files_in - .unwrap_or_default() - .into_iter() - .map(|(host, guest)| FileInput { - host_path: std::path::PathBuf::from(host), - guest_path: guest, - }) - .collect(); - - let expect_vec: Vec = expect - .unwrap_or_default() - .into_iter() - .map(|pat| FileOutput { pattern: pat }) - .collect(); - - let result = py.allow_threads(|| { - let runner = VMRunner::new(); - runner.execute_python_code(&code, &config, files_in_vec, expect_vec) - }); - - match result { - Ok(execution_result) => { - let mut stdout = execution_result.stdout; - let mut stderr = execution_result.stderr; - let exit_code = execution_result.exit_code; - - // Se houve erro e STDERR vier vazio (krunvm pode colapsar streams), duplica STDOUT. - if exit_code != 0 && stderr.trim().is_empty() && !stdout.trim().is_empty() { - stderr = stdout.clone(); - } - - let dict = PyDict::new_bound(py); - dict.set_item("stdout", stdout)?; - dict.set_item("stderr", stderr)?; - dict.set_item("exit_code", exit_code)?; - let exec_ms = std::cmp::max(1, execution_result.execution_time.as_millis() as u64); - dict.set_item("execution_time_ms", exec_ms)?; - dict.set_item("image_used", execution_result.image_used)?; - - let artifacts_py = pyo3::types::PyList::empty_bound(py); - for a in execution_result.artifacts { - let a_dict = PyDict::new_bound(py); - a_dict.set_item("guest_path", a.guest_path)?; - a_dict.set_item("host_path", a.host_path.to_string_lossy().to_string())?; - a_dict.set_item("size_bytes", a.size_bytes)?; - if let Some(content) = a.content { - a_dict.set_item("content", pyo3::types::PyBytes::new_bound(py, &content))?; - } - artifacts_py.append(a_dict)?; - } - dict.set_item("artifacts", artifacts_py)?; - Ok(dict.into()) - } - Err(e) => Err(PyVMError::new_err(format!("Execution error: {}", e))), - } -} - -#[pyfunction] -fn run_with_config(py: Python, code: String, config: &Bound) -> PyResult { - let image = config.get_item("image")?.and_then(|v| v.extract::().ok()); - let cpus = config.get_item("cpus")?.and_then(|v| v.extract::().ok()).unwrap_or(1); - let memory_mb = config.get_item("memory_mb")?.and_then(|v| v.extract::().ok()).unwrap_or(512); - let env = config.get_item("env")?.and_then(|v| v.extract::>().ok()).unwrap_or_default(); - let timeout_seconds = config.get_item("timeout_seconds")?.and_then(|v| v.extract::().ok()).unwrap_or(30); - let workdir = config.get_item("workdir")?.and_then(|v| v.extract::().ok()).unwrap_or_else(|| "/work".to_string()); - let python_args = config.get_item("python_args")?.and_then(|v| v.extract::>().ok()).unwrap_or_else(|| vec!["-u".to_string()]); - let network = config.get_item("network")?.and_then(|v| v.extract::().ok()).unwrap_or(false); - let ports = config.get_item("ports")?.and_then(|v| v.extract::>().ok()).unwrap_or_default(); - let files_in = config.get_item("files_in")?.and_then(|v| v.extract::>().ok()).unwrap_or_default(); - let expect = config.get_item("expect")?.and_then(|v| v.extract::>().ok()).unwrap_or_default(); - let max_bytes_inline = config.get_item("max_bytes_inline")?.and_then(|v| v.extract::().ok()).unwrap_or(1024*1024); - - let vm_config = VMConfig { - image, - cpus, - memory_mb, - env, - workdir, - timeout: Duration::from_secs(timeout_seconds), - network, - ports, - python_args, - max_bytes_inline, - }; - - if !vm_config.workdir.starts_with('/') || vm_config.workdir.matches('/').count() > 1 { - return Err(PyVMError::new_err("workdir must be top-level (e.g., /work)".to_string())); - } - let files_in_vec: Vec = files_in - .into_iter() - .map(|(host, guest)| FileInput { - host_path: std::path::PathBuf::from(host), - guest_path: guest, - }) - .collect(); - - let expect_vec: Vec = expect.into_iter().map(|pat| FileOutput { pattern: pat }).collect(); - - let result = py.allow_threads(|| { - let runner = VMRunner::new(); - runner.execute_python_code(&code, &vm_config, files_in_vec, expect_vec) - }); +mod vmm; +mod types; +mod error; - match result { - Ok(execution_result) => { - let mut stdout = execution_result.stdout; - let mut stderr = execution_result.stderr; - let exit_code = execution_result.exit_code; - if exit_code != 0 && stderr.trim().is_empty() && !stdout.trim().is_empty() { - stderr = stdout.clone(); - } +use crate::types::{ImageHandle, RunOptions, RunResult}; - let dict = PyDict::new_bound(py); - dict.set_item("stdout", stdout)?; - dict.set_item("stderr", stderr)?; - dict.set_item("exit_code", exit_code)?; - let exec_ms = std::cmp::max(1, execution_result.execution_time.as_millis() as u64); - dict.set_item("execution_time_ms", exec_ms)?; - dict.set_item("image_used", execution_result.image_used)?; - let artifacts_py = pyo3::types::PyList::empty_bound(py); - for a in execution_result.artifacts { - let a_dict = PyDict::new_bound(py); - a_dict.set_item("guest_path", a.guest_path)?; - a_dict.set_item("host_path", a.host_path.to_string_lossy().to_string())?; - a_dict.set_item("size_bytes", a.size_bytes)?; - if let Some(content) = a.content { a_dict.set_item("content", pyo3::types::PyBytes::new_bound(py, &content))?; } - artifacts_py.append(a_dict)?; - } - dict.set_item("artifacts", artifacts_py)?; - Ok(dict.into()) - } - Err(e) => Err(PyVMError::new_err(format!("Execution error: {}", e))), - } -} #[pyfunction] -#[pyo3(signature = (image=None))] -fn prepare_image(py: Python, image: Option) -> PyResult { - let result: Result = py.allow_threads(|| { - let resolver = ImageResolver::new(); - match image { - None => { - resolver.import_embedded_now()?; - Ok(true) - } - Some(img) => { - let validated = resolver.resolve_image_ref(Some(&img))?; - let is_docker_like = img.starts_with("docker://") || !img.starts_with("oci:"); - if is_docker_like { - let runner = VMRunner::new(); - runner.pre_pull_image(&validated)?; - } - Ok(true) - } - } - }); - - match result { - Ok(v) => Ok(v), - Err(e) => Err(PyVMError::new_err(format!("Error preparing image: {}", e))), - } +fn prepare_image<'py>(_py: Python<'py>, meta: Bound<'py, PyDict>) -> PyResult { + // TODO: validar dict (kernel, rootfs_img, initrd opcional, cache_key) + ImageHandle::from_pydict(&meta) } -#[pyfunction] -fn list_cached_images(py: Python) -> PyResult> { - let result = py.allow_threads(|| { - let resolver = ImageResolver::new(); - resolver.list_cached_images() - }); - match result { - Ok(images) => Ok(images), - Err(e) => Err(PyVMError::new_err(format!("Error listing images: {}", e))), - } -} #[pyfunction] -fn clear_cache(py: Python) -> PyResult { - let result = py.allow_threads(|| { - let resolver = ImageResolver::new(); - resolver.clear_cache() - }); - match result { - Ok(_) => Ok(true), - Err(e) => Err(PyVMError::new_err(format!("Error clearing cache: {}", e))), - } +#[pyo3(signature = (image, code, opts=None))] +fn run<'py>(py: Python<'py>, image: &ImageHandle, code: &str, opts: Option>) -> PyResult { + let opts = RunOptions::from_py(opts.as_ref())?; + let res = py + .allow_threads(|| vmm::run::run_vm(image, code, &opts)) + .map_err(error::pyerr)?; + Ok(res) } -#[pyfunction] -fn doctor(py: Python) -> PyResult { - let dict: Bound<'_, PyDict> = PyDict::new_bound(py); - - let krunvm_available = std::process::Command::new("krunvm").arg("--version").output().is_ok(); - let buildah_available = std::process::Command::new("buildah").arg("--version").output().is_ok(); - let skopeo_available = std::process::Command::new("skopeo").arg("--version").output().is_ok(); - let kvm_available = std::path::Path::new("/dev/kvm").exists(); - - let offline_available = wheel_resources::WheelResources::check_embedded_image_available(py, "python-basic") - .unwrap_or(false); - - let mut embedded_imported = false; - if offline_available { - if let Ok(resolver) = std::panic::catch_unwind(|| ImageResolver::new()) { - if let Ok(imported) = resolver.embedded_is_imported() { - embedded_imported = imported; - } - } - } - - let mut offline_message: Option = None; - if !offline_available { - if let Ok(Some(oci_path)) = wheel_resources::WheelResources::find_embedded_data_path(py) { - let layout = oci_path.join("oci-layout"); - let index = oci_path.join("index.json"); - let blobs = oci_path.join("blobs").join("sha256"); - let mut missing = vec![]; - if !layout.exists() { missing.push("oci-layout"); } - if !index.exists() { missing.push("index.json"); } - if !blobs.exists() { missing.push("blobs/sha256"); } - if !missing.is_empty() { - offline_message = Some(format!( - "Embedded OCI layout incomplete at {}: missing {}", - oci_path.to_string_lossy(), - missing.join(", ") - )); - } else { - offline_message = Some("Failed to validate embedded OCI layout".to_string()); - } - } else { - offline_message = Some("Embedded OCI image not found (flashvm/data/oci)".to_string()); - } - } - - dict.set_item("krunvm", krunvm_available)?; - dict.set_item("buildah", buildah_available)?; - dict.set_item("skopeo", skopeo_available)?; - dict.set_item("kvm", kvm_available)?; - dict.set_item("offline_mode", offline_available)?; - dict.set_item("embedded_imported", embedded_imported)?; - if !skopeo_available { - dict.set_item("note", "skopeo not found; import will use buildah fallback and may be slower")?; - } - if let Some(msg) = offline_message { dict.set_item("offline_message", msg)?; } - dict.set_item("ready", krunvm_available && buildah_available && kvm_available)?; - - Ok(dict.into()) -} #[pymodule] -#[pyo3(name = "_core")] -fn flashvm(m: &Bound<'_, PyModule>) -> PyResult<()> { - m.add_function(wrap_pyfunction!(run, m)?)?; - m.add_function(wrap_pyfunction!(run_with_config, m)?)?; - m.add_function(wrap_pyfunction!(prepare_image, m)?)?; - m.add_function(wrap_pyfunction!(list_cached_images, m)?)?; - m.add_function(wrap_pyfunction!(clear_cache, m)?)?; - m.add_function(wrap_pyfunction!(doctor, m)?)?; - m.add_function(wrap_pyfunction!(find_embedded_data_path, m)?)?; +fn flashvm_native<'py>(_py: Python<'py>, m: Bound<'py, PyModule>) -> PyResult<()> { + m.add_function(wrap_pyfunction_bound!(prepare_image, &m)?)?; + m.add_function(wrap_pyfunction_bound!(run, &m)?)?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; Ok(()) -} +} \ No newline at end of file diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..e1b557a --- /dev/null +++ b/src/types.rs @@ -0,0 +1,81 @@ +use serde::{Deserialize, Serialize}; +use pyo3::prelude::*; +use pyo3::types::PyDict; + + +#[pyclass] +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ImageHandle { + #[pyo3(get)] pub kernel: String, + #[pyo3(get)] pub rootfs_img: String, + #[pyo3(get)] pub initrd: Option, + #[pyo3(get)] pub cache_key: Option, +} + + +impl ImageHandle { + pub fn from_pydict<'py>(d: &pyo3::Bound<'py, PyDict>) -> PyResult { + // TODO: validação robusta + normalização de paths + Ok(Self { + kernel: d + .get_item("kernel")? + .ok_or_else(|| pyo3::exceptions::PyValueError::new_err("missing kernel"))? + .extract()?, + rootfs_img: d + .get_item("rootfs_img")? + .ok_or_else(|| pyo3::exceptions::PyValueError::new_err("missing rootfs_img"))? + .extract()?, + initrd: d.get_item("initrd")?.map(|v| v.extract()).transpose()?, + cache_key: d.get_item("cache_key")?.map(|v| v.extract()).transpose()?, + }) + } +} + + +#[pyclass] +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct RunOptions { + #[pyo3(get)] pub cpus: u8, + #[pyo3(get)] pub mem_mb: u32, + #[pyo3(get)] pub timeout_ms: Option, + #[pyo3(get)] pub output_mode: String, // "diff" | "all" | "none" | "paths" +} + + +impl RunOptions { + pub fn from_py<'py>(d: Option<&pyo3::Bound<'py, PyDict>>) -> PyResult { + let cpus: u8 = match d { + Some(x) => x.get_item("cpus")?.map(|v| v.extract().unwrap_or(1)).unwrap_or(1), + None => 1, + }; + let mem_mb: u32 = match d { + Some(x) => x.get_item("mem_mb")?.map(|v| v.extract().unwrap_or(512)).unwrap_or(512), + None => 512, + }; + let timeout_ms: Option = match d { + Some(x) => x + .get_item("timeout_ms")? + .map(|v| v.extract().unwrap_or(0)) + .filter(|&t| t > 0), + None => None, + }; + let output_mode: String = match d { + Some(x) => x + .get_item("output")? + .map(|v| v.extract().unwrap_or("diff".to_string())) + .unwrap_or("diff".into()), + None => "diff".into(), + }; + Ok(Self { cpus, mem_mb, timeout_ms, output_mode }) + } +} + + +#[pyclass] +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct RunResult { + #[pyo3(get)] pub stdout: String, + #[pyo3(get)] pub stderr: String, + #[pyo3(get)] pub exit_status: i32, + #[pyo3(get)] pub outputs_dir: Option, +} \ No newline at end of file diff --git a/src/vm_runner.rs b/src/vm_runner.rs deleted file mode 100644 index 4ff77f7..0000000 --- a/src/vm_runner.rs +++ /dev/null @@ -1,460 +0,0 @@ -use crate::config::{Artifact, ExecutionResult, FileInput, FileOutput, VMConfig}; -use crate::error::VMError; -use crate::image_resolver::ImageResolver; -use anyhow::Result; -use glob::glob; -use log::{debug, info}; -use serde_json; -use std::fs; -use std::io::Write; -use std::os::unix::process::ExitStatusExt; -use std::path::Path; -use std::process::{Command, Stdio}; -use std::time::{Duration, Instant}; -use tempfile::{NamedTempFile, TempDir}; -use uuid::Uuid; - -fn shell_escape(s: &str) -> String { - if s.chars().all(|c| c.is_ascii_alphanumeric() || "/-_.:@".contains(c)) { - s.to_string() - } else { - format!("'{}'", s.replace('\'', "'\\''")) - } -} - -struct WorkDirectories { - _temp_base: TempDir, - input_dir: std::path::PathBuf, - output_dir: std::path::PathBuf, - _tmp_dir: std::path::PathBuf, - scripts_dir: std::path::PathBuf, -} - -pub struct VMRunner { - image_resolver: ImageResolver, -} - -impl VMRunner { - pub fn new() -> Self { - Self { - image_resolver: ImageResolver::new(), - } - } - - pub fn pre_pull_image(&self, image_ref: &str) -> Result<(), VMError> { - let resolved = self.image_resolver.resolve_image_ref(Some(image_ref))?; - let normalized = self.normalize_image_for_krunvm(&resolved)?; - let vm_name = format!("prepull-{}", &Uuid::new_v4().to_string()[..8]); - let script = format!( - "set -e\n\ - krunvm create --cpus 1 --mem 256 --workdir /work --name {} {} >/dev/null\n\ - krunvm delete -f {} >/dev/null 2>&1 || krunvm delete {} >/dev/null 2>&1 || true\n", - shell_escape(&vm_name), - normalized, - shell_escape(&vm_name), - shell_escape(&vm_name), - ); - let out = self.run_in_buildah_unshare_capture(&script)?; - if !out.success { - return Err(VMError::VMConfiguration(format!("pre-pull failed: {}", out.stderr))); - } - Ok(()) - } - - pub fn execute_python_code( - &self, - code: &str, - config: &VMConfig, - files_in: Vec, - expect: Vec, - ) -> Result { - let start_time = Instant::now(); - - info!("Starting execution with config: {:?}", config); - - self.check_dependencies()?; - - // Resolve image → nome aceitável pelo krunvm - let resolved_image = self.image_resolver.resolve_image_ref(config.image.as_deref())?; - let image_ref = self.normalize_image_for_krunvm(&resolved_image)?; - info!("Using image: {}", image_ref); - - let temp_dirs = self.setup_work_directories()?; - self.prepare_input_files(&files_in, &temp_dirs.input_dir)?; - let script_file = self.create_python_script(code)?; - - let vm_result = self.run_vm_with_krunvm(&image_ref, &script_file, config, &temp_dirs)?; - let artifacts = self.collect_artifacts(&expect, &temp_dirs.output_dir, config.max_bytes_inline)?; - let execution_time = start_time.elapsed(); - - Ok(ExecutionResult { - stdout: vm_result.stdout, - stderr: vm_result.stderr, - exit_code: vm_result.exit_code, - execution_time, - artifacts, - image_used: image_ref, - }) - } - - fn normalize_image_for_krunvm(&self, image: &str) -> Result { - if let Some(name) = image.strip_prefix("containers-storage:") { - return Ok(name.to_string()); - } - if image.starts_with("oci:") { - let tmp_name = format!("localhost/flashvm:imported-{}", &Uuid::new_v4().to_string()[..8]); - self.import_oci_to_storage(image, &tmp_name)?; - return Ok(tmp_name); - } - Ok(image.to_string()) - } - - fn import_oci_to_storage(&self, oci_ref: &str, dest_name: &str) -> Result<(), VMError> { - if self.command_exists("skopeo") { - let cmd = format!( - "skopeo copy --insecure-policy '{}' 'containers-storage:{}'", - oci_ref, dest_name - ); - if self.run_in_buildah_unshare(&cmd)? { - return Ok(()); - } - } - let from_out = self.run_in_buildah_unshare_capture(&format!("buildah from '{}'", oci_ref))?; - if !from_out.success { - return Err(VMError::Execution(format!("buildah from failed: {}", from_out.stderr))); - } - let container = from_out.stdout.trim(); - if container.is_empty() { - return Err(VMError::Execution("buildah from did not return a name".to_string())); - } - let ok_commit = - self.run_in_buildah_unshare(&format!("buildah commit '{}' '{}'", container, dest_name))?; - let _ = self.run_in_buildah_unshare(&format!("buildah rm '{}'", container)); - if !ok_commit { - return Err(VMError::Execution("buildah commit failed".to_string())); - } - Ok(()) - } - - fn check_dependencies(&self) -> Result<(), VMError> { - if !self.command_exists("krunvm") { - return Err(VMError::MissingDependency( - "krunvm not found. Please install krunvm to continue.".to_string(), - )); - } - if !self.command_exists("buildah") { - return Err(VMError::MissingDependency( - "buildah not found. Required for rootless.".to_string(), - )); - } - if !Path::new("/dev/kvm").exists() { - return Err(VMError::MissingDependency( - "KVM not available. Ensure virtualization is enabled.".to_string(), - )); - } - Ok(()) - } - - fn command_exists(&self, cmd: &str) -> bool { - Command::new("which") - .arg(cmd) - .output() - .map(|o| o.status.success()) - .unwrap_or(false) - } - - fn run_in_buildah_unshare(&self, sh_cmd: &str) -> Result { - debug!("Executing: buildah unshare sh -c '{}'", sh_cmd); - let status = Command::new("buildah") - .args(["unshare", "sh", "-c", sh_cmd]) - .status() - .map_err(|e| VMError::Execution(format!("Failed to execute command: {}", e)))?; - Ok(status.success()) - } - - fn run_in_buildah_unshare_capture(&self, sh_cmd: &str) -> Result { - debug!("Executing: buildah unshare sh -c '{}'", sh_cmd); - let output = Command::new("buildah") - .args(["unshare", "sh", "-c", sh_cmd]) - .output() - .map_err(|e| VMError::Execution(format!("Failed to execute command: {}", e)))?; - Ok(Captured { - stdout: String::from_utf8_lossy(&output.stdout).to_string(), - stderr: String::from_utf8_lossy(&output.stderr).to_string(), - success: output.status.success(), - exit_code: output.status.code(), - timed_out: false, - }) - } - - fn run_in_buildah_unshare_capture_timeout( - &self, - sh_cmd: &str, - timeout: Duration, - ) -> Result { - debug!("Executing (timeout={:?}): buildah unshare sh -c '{}'", timeout, sh_cmd); - let mut child = Command::new("buildah") - .args(["unshare", "sh", "-c", sh_cmd]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .map_err(|e| VMError::Execution(format!("Failed to spawn command: {}", e)))?; - - let mut stdout = child - .stdout - .take() - .ok_or_else(|| VMError::Execution("Failed to capture stdout".to_string()))?; - let mut stderr = child - .stderr - .take() - .ok_or_else(|| VMError::Execution("Failed to capture stderr".to_string()))?; - - let stdout_handle = std::thread::spawn(move || { - let mut buf = Vec::new(); - let _ = std::io::copy(&mut stdout, &mut buf); - buf - }); - let stderr_handle = std::thread::spawn(move || { - let mut buf = Vec::new(); - let _ = std::io::copy(&mut stderr, &mut buf); - buf - }); - - let start = Instant::now(); - let mut timed_out = false; - let status = loop { - match child.try_wait() { - Ok(Some(status)) => break status, - Ok(None) => { - if start.elapsed() >= timeout { - timed_out = true; - let _ = child.kill(); - // Single wait; if it fails, synthesize a 124 exit status (like coreutils timeout) - break child - .wait() - .unwrap_or_else(|_| std::process::ExitStatus::from_raw(124 << 8)); - } - std::thread::sleep(Duration::from_millis(25)); - } - Err(e) => { - return Err(VMError::Execution(format!("Failed to wait for process: {}", e))); - } - } - }; - - let out_v = stdout_handle.join().unwrap_or_default(); - let err_v = stderr_handle.join().unwrap_or_default(); - - let mut exit_code = status.code(); - if timed_out { - // padroniza como 124 (semelhante a coreutils timeout) - exit_code = Some(124); - } - - Ok(Captured { - stdout: String::from_utf8_lossy(&out_v).to_string(), - stderr: String::from_utf8_lossy(&err_v).to_string(), - success: status.success(), - exit_code, - timed_out, - }) - } - - fn setup_work_directories(&self) -> Result { - let temp_base = TempDir::new().map_err(VMError::IO)?; - let input_dir = temp_base.path().join("in"); - let output_dir = temp_base.path().join("out"); - let tmp_dir = temp_base.path().join("tmp"); - let scripts_dir = temp_base.path().join("scripts"); - fs::create_dir_all(&input_dir)?; - fs::create_dir_all(&output_dir)?; - fs::create_dir_all(&tmp_dir)?; - fs::create_dir_all(&scripts_dir)?; - Ok(WorkDirectories { - input_dir, - output_dir, - _tmp_dir: tmp_dir, - scripts_dir, - _temp_base: temp_base, - }) - } - - fn prepare_input_files(&self, files_in: &[FileInput], input_dir: &Path) -> Result<(), VMError> { - for file_input in files_in { - let target_path = input_dir.join(&file_input.guest_path); - if let Some(parent) = target_path.parent() { - fs::create_dir_all(parent)?; - } - fs::copy(&file_input.host_path, &target_path).map_err(VMError::IO)?; - debug!("File copied: {:?} -> {:?}", file_input.host_path, target_path); - } - Ok(()) - } - - fn create_python_script(&self, code: &str) -> Result { - let mut script_file = NamedTempFile::new().map_err(VMError::IO)?; - script_file.write_all(code.as_bytes()).map_err(VMError::IO)?; - script_file.flush().map_err(VMError::IO)?; - Ok(script_file) - } - - fn create_guest_runner( - &self, - config: &VMConfig, - scripts_dir: &Path, - main_script: &str, - ) -> Result { - let runner_path = scripts_dir.join("run.py"); - let env_json = serde_json::to_string(&config.env).map_err(|e| VMError::Execution(e.to_string()))?; - let args_json = - serde_json::to_string(&config.python_args).map_err(|e| VMError::Execution(e.to_string()))?; - let runner_code = format!( - "#!/usr/bin/env python3\n\ - import os, sys, json, subprocess\n\ - ENV=json.loads(r'''{}''')\n\ - PY_ARGS=json.loads(r'''{}''')\n\ - os.environ.update({{k:str(v) for k,v in ENV.items()}})\n\ - cmd=['/usr/bin/env','python3']+PY_ARGS+['/work/scripts/{}']\n\ - res=subprocess.run(cmd)\n\ - sys.exit(res.returncode)\n", - env_json, args_json, main_script - ); - fs::write(&runner_path, runner_code.as_bytes())?; - Ok("/work/scripts/run.py".to_string()) - } - - fn run_vm_with_krunvm( - &self, - image_ref: &str, - script_file: &NamedTempFile, - config: &VMConfig, - work_dirs: &WorkDirectories, - ) -> Result { - // Copia o script principal para /work/scripts/main.py - let script_filename = "main.py"; - let script_target = work_dirs.scripts_dir.join(script_filename); - fs::copy(script_file.path(), &script_target)?; - if !script_target.exists() { - return Err(VMError::IO(std::io::Error::new( - std::io::ErrorKind::NotFound, - format!("Script was not copied correctly to {:?}", script_target), - ))); - } - - let runner_path_guest = self.create_guest_runner(config, &work_dirs.scripts_dir, script_filename)?; - - // Comando dentro da VM: rodar diretamente python sem shell - let start_cmd = format!( - "krunvm start {vm} /usr/bin/env python3 {runner}", - vm = "{vm}", // placeholder trocado no format abaixo - runner = shell_escape(&runner_path_guest), - ); - - // Script de controle (create → start com retries → delete) - let vm_name = format!("flashvm-{}", &Uuid::new_v4().to_string()[..8]); - let work_base = work_dirs._temp_base.path().to_string_lossy().to_string(); - let mut ports_args = String::new(); - if config.network { - for (host, guest) in &config.ports { - ports_args.push_str(&format!(" --port {}:{}", host, guest)); - } - } - - let shell_script = format!( - "set -e\n\ - krunvm create --cpus {cpus} --mem {mem} --workdir {workdir} --name {vm} --volume '{work}:/work'{ports} {image} >/dev/null\n\ - set +e\n\ - tries=0\n\ - ec=1\n\ - while [ $tries -lt 3 ]; do\n\ - {start}\n\ - ec=$?\n\ - [ $ec -eq 0 ] && break\n\ - tries=$((tries+1))\n\ - sleep 0.15\n\ - done\n\ - set -e\n\ - krunvm delete -f {vm} >/dev/null 2>&1 || krunvm delete {vm} >/dev/null 2>&1 || true\n\ - exit $ec\n", - cpus = config.cpus, - mem = config.memory_mb, - workdir = shell_escape(&config.workdir), - vm = shell_escape(&vm_name), - work = shell_escape(&work_base), - ports = ports_args, - image = image_ref, - start = start_cmd.replace("{vm}", &shell_escape(&vm_name)), - ); - - // Timeout total = timeout de usuário + pequena folga p/ create/delete - let hard_timeout = config.timeout + Duration::from_secs(2); - let out = self.run_in_buildah_unshare_capture_timeout(&shell_script, hard_timeout)?; - - // Cleanup extra se houve timeout ou falha antes do delete interno - if out.timed_out || !out.success { - let _ = self.run_in_buildah_unshare(&format!( - "krunvm delete -f {vm} >/dev/null 2>&1 || krunvm delete {vm} >/dev/null 2>&1 || true", - vm = shell_escape(&vm_name) - )); - } - - Ok(VMExecutionResult { - stdout: out.stdout, - stderr: out.stderr, - exit_code: out.exit_code.unwrap_or(-1), - success: out.success, - }) - } - - fn collect_artifacts( - &self, - expect: &[FileOutput], - output_dir: &Path, - max_inline: u64, - ) -> Result, VMError> { - let mut artifacts = Vec::new(); - for file_output in expect { - let pattern = output_dir.join(&file_output.pattern); - let pattern_str = pattern.to_string_lossy().to_string(); - for entry in glob(&pattern_str).map_err(|e| VMError::Execution(e.msg.to_string()))? { - if let Ok(path) = entry { - if path.is_file() { - let metadata = fs::metadata(&path)?; - let size_bytes = metadata.len(); - let content = if size_bytes <= max_inline { - Some(fs::read(&path)?) - } else { - None - }; - let guest_rel = path.strip_prefix(output_dir).unwrap_or(&path); - let guest_path = format!("out/{}", guest_rel.to_string_lossy()); - artifacts.push(Artifact { - guest_path, - host_path: path.clone(), - size_bytes, - content, - }); - } - } - } - } - Ok(artifacts) - } -} - -#[derive(Debug)] -struct VMExecutionResult { - stdout: String, - stderr: String, - exit_code: i32, - success: bool, -} - -#[derive(Debug)] -struct Captured { - stdout: String, - stderr: String, - success: bool, - exit_code: Option, - timed_out: bool, -} diff --git a/src/vmm/boot.rs b/src/vmm/boot.rs new file mode 100644 index 0000000..ca8abf7 --- /dev/null +++ b/src/vmm/boot.rs @@ -0,0 +1,25 @@ +use anyhow::Result; +use linux_loader::loader::{Elf, KernelLoader}; +use linux_loader::cmdline::Cmdline; +use vm_memory::{GuestMemoryMmap, GuestAddress}; +use std::path::Path; + + +pub struct BootInfo { pub entry: GuestAddress, pub cmdline_addr: GuestAddress } + + +pub fn load_kernel(gm: &GuestMemoryMmap, kernel_path: &Path, cmdline: &str) -> Result { + // TODO: suportar bzImage; por ora, ELF vmlinux + let mut kernel_image = std::fs::File::open(kernel_path)?; + let k = Elf::load(gm, None, &mut kernel_image, None)?; + + + // Cmdline + let mut cmd = Cmdline::new(1024)?; + cmd.insert_str(cmdline)?; + // TODO: alocar e escrever cmdline em memória convidada + let cmdline_addr = GuestAddress(0x20000); // placeholder + + + Ok(BootInfo { entry: k.kernel_load, cmdline_addr }) +} \ No newline at end of file diff --git a/src/vmm/devices.rs b/src/vmm/devices.rs new file mode 100644 index 0000000..2277e55 --- /dev/null +++ b/src/vmm/devices.rs @@ -0,0 +1,18 @@ +use anyhow::Result; + + +pub struct BlockSpec { pub path: String, pub read_only: bool } + + +pub struct DevicesCfg { + pub console_stdio: bool, + pub rootfs: BlockSpec, + pub workspace: Option, +} + + +pub fn attach_devices(_cfg: &DevicesCfg) -> Result<()> { + // TODO: instanciar vm_superio::Serial (ttyS0) e mapear em PIO/MMIO + // TODO: instanciar virtio-blk (rootfs/workspace) e registrar no event-manager + Ok(()) +} \ No newline at end of file diff --git a/src/vmm/event_loop.rs b/src/vmm/event_loop.rs new file mode 100644 index 0000000..1ba3c0f --- /dev/null +++ b/src/vmm/event_loop.rs @@ -0,0 +1,13 @@ +use anyhow::Result; + + +pub struct VmLoop; + + +impl VmLoop { + pub fn new() -> Result { Ok(Self) } + pub fn run_until_exit(&mut self, _timeout_ms: Option) -> Result { + // TODO: dirigir o loop com event-manager + Ok(0) + } +} \ No newline at end of file diff --git a/src/vmm/kvm_ctx.rs b/src/vmm/kvm_ctx.rs new file mode 100644 index 0000000..ea61485 --- /dev/null +++ b/src/vmm/kvm_ctx.rs @@ -0,0 +1,20 @@ +use anyhow::Result; +use kvm_ioctls::{Kvm, VmFd, VcpuFd}; + + +pub struct KvmContext { +pub kvm: Kvm, +pub vm: VmFd, +pub vcpus: Vec, +} + + +impl KvmContext { +pub fn new(cpus: u8) -> Result { + let kvm = Kvm::new()?; + let vm = kvm.create_vm()?; + let mut vcpus = Vec::new(); + for _i in 0..cpus { vcpus.push(vm.create_vcpu(0)?); /* TODO: regs/sregs/APIC por vCPU */ } + // TODO: IRQ routing, pit/clk mínimos se necessário + Ok(Self { kvm, vm, vcpus }) +}} diff --git a/src/vmm/memory.rs b/src/vmm/memory.rs new file mode 100644 index 0000000..eec0c6f --- /dev/null +++ b/src/vmm/memory.rs @@ -0,0 +1,14 @@ +use anyhow::Result; +use vm_memory::{GuestMemoryMmap, GuestAddress}; + + +pub struct GuestMem { pub mem: GuestMemoryMmap } + + +impl GuestMem { + pub fn create(mem_mb: u32) -> Result { + let size_u64 = (mem_mb as u64) * 1024 * 1024; + let size: usize = size_u64.try_into().map_err(|_| anyhow::anyhow!("mem size too big"))?; + let gm = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), size)])?; + Ok(Self { mem: gm }) +}} \ No newline at end of file diff --git a/src/vmm/mod.rs b/src/vmm/mod.rs new file mode 100644 index 0000000..0b971e9 --- /dev/null +++ b/src/vmm/mod.rs @@ -0,0 +1,8 @@ +pub mod kvm_ctx; +pub mod memory; +pub mod boot; +pub mod devices; +pub mod event_loop; +pub mod run; +#[cfg(feature = "x86_64")] pub mod platform_x86; +#[cfg(feature = "aarch64")] pub mod platform_aarch64; \ No newline at end of file diff --git a/src/vmm/platform_aarch64.rs b/src/vmm/platform_aarch64.rs new file mode 100644 index 0000000..7785411 --- /dev/null +++ b/src/vmm/platform_aarch64.rs @@ -0,0 +1,4 @@ +#[allow(dead_code)] +pub fn build_fdt() { + // TODO: vm-fdt: memória, CPUs, virtio-mmio, chosen/bootargs +} \ No newline at end of file diff --git a/src/vmm/platform_x86.rs b/src/vmm/platform_x86.rs new file mode 100644 index 0000000..39e630b --- /dev/null +++ b/src/vmm/platform_x86.rs @@ -0,0 +1,4 @@ +#[allow(dead_code)] +pub fn build_acpi_tables() { + // TODO: acpi_tables: DSDT, MADT, FADT mínimos +} \ No newline at end of file diff --git a/src/vmm/run.rs b/src/vmm/run.rs new file mode 100644 index 0000000..c8c59a0 --- /dev/null +++ b/src/vmm/run.rs @@ -0,0 +1,36 @@ +use anyhow::Result; +use crate::types::{ImageHandle, RunOptions, RunResult}; +use super::{kvm_ctx::KvmContext, memory::GuestMem, boot, event_loop::VmLoop}; +use std::path::PathBuf; + + +pub fn run_vm(image: &ImageHandle, code: &str, opts: &RunOptions) -> Result { + // 1) memória + KVM + let gm = GuestMem::create(opts.mem_mb)?; + let _kvmx = KvmContext::new(opts.cpus)?; + + + // 2) cmdline mínima para guest-init + let cmdline = format!( + "console=ttyS0 root=/dev/vda ro init=/sbin/init FLASHVM_MODE=run FLASHVM_CODE_LEN={}", + code.len() + ); + + + // 3) carregar kernel + let kernel_path = PathBuf::from(&image.kernel); + let _boot = boot::load_kernel(&gm.mem, &kernel_path, &cmdline)?; + + + // 4) anexar devices (serial + vda=rootfs RO + vdb=workspace RW) + // TODO: criar workspace.img e gravar código/entrypoint + + + // 5) loop até saída/timeout + let mut vml = VmLoop::new()?; + let status = vml.run_until_exit(opts.timeout_ms)?; + + + // 6) coletar stdout/stderr + artefatos do workspace + Ok(RunResult { stdout: String::new(), stderr: String::new(), exit_status: status, outputs_dir: None }) +} \ No newline at end of file diff --git a/src/wheel_resources.rs b/src/wheel_resources.rs deleted file mode 100644 index 09c5d9c..0000000 --- a/src/wheel_resources.rs +++ /dev/null @@ -1,74 +0,0 @@ -use pyo3::prelude::*; -use std::path::PathBuf; - -#[pyfunction] -pub fn find_embedded_data_path() -> PyResult { - Python::with_gil(|py| { - let importlib_resources = py.import_bound("importlib.resources")?; - let pathlib = py.import_bound("pathlib")?; - - let package_files = importlib_resources - .getattr("files")? - .call1(("flashvm",))?; - - let data_path = package_files.call_method1("joinpath", ("data",))?; - let oci_path = data_path.call_method1("joinpath", ("oci",))?; - - let as_file = importlib_resources.getattr("as_file")?; - let context_manager = as_file.call1((oci_path,))?; - let enter_method = context_manager.getattr("__enter__")?; - let oci_real_path = enter_method.call0()?; - - let path_obj = pathlib.getattr("Path")?.call1((oci_real_path,))?; - let path_str: String = path_obj.call_method0("__str__")?.extract()?; - - let oci_layout_file = path_obj.call_method1("joinpath", ("oci-layout",))?; - let index_file = path_obj.call_method1("joinpath", ("index.json",))?; - let blobs_dir = path_obj.call_method1("joinpath", ("blobs",))?; - let blobs_sha256 = blobs_dir.call_method1("joinpath", ("sha256",))?; - - let oci_layout_exists: bool = oci_layout_file.call_method0("exists")?.extract()?; - let index_exists: bool = index_file.call_method0("exists")?.extract()?; - let blobs_exists: bool = blobs_dir.call_method0("exists")?.extract()?; - let blobs_sha256_exists: bool = blobs_sha256.call_method0("exists")?.extract()?; - - let exit_method = context_manager.getattr("__exit__")?; - exit_method.call1((py.None(), py.None(), py.None()))?; - - if oci_layout_exists && index_exists && blobs_exists && blobs_sha256_exists { - Ok(format!("oci:{}:python-basic", path_str)) - } else { - Err(pyo3::exceptions::PyRuntimeError::new_err(format!( - "Invalid OCI structure at {}: oci-layout={}, index.json={}, blobs={}, blobs/sha256={}", - path_str, oci_layout_exists, index_exists, blobs_exists, blobs_sha256_exists - ))) - } - }) -} - -pub struct WheelResources; - -impl WheelResources { - pub fn find_embedded_data_path(_py: Python) -> PyResult> { - match find_embedded_data_path() { - Ok(oci_ref) => { - if let Some((path_part, _tag)) = oci_ref - .strip_prefix("oci:") - .and_then(|s| s.rsplit_once(':')) - { - Ok(Some(PathBuf::from(path_part))) - } else { - Ok(None) - } - } - Err(_) => Ok(None), - } - } - - pub fn check_embedded_image_available(_py: Python, _tag: &str) -> PyResult { - match find_embedded_data_path() { - Ok(_) => Ok(true), - Err(_) => Ok(false), - } - } -} diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index f491309..0000000 --- a/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# Tests package for flashvm diff --git a/tests/benchmark/test_performance.py b/tests/benchmark/test_performance.py deleted file mode 100644 index f2dd849..0000000 --- a/tests/benchmark/test_performance.py +++ /dev/null @@ -1,375 +0,0 @@ -""" -Benchmark tests for flashvm performance analysis. - -These tests measure execution times and resource usage patterns. -""" - -import pytest - - -class TestPerformanceBenchmarks: - """Performance benchmarks for VM execution.""" - - @pytest.mark.benchmark - @pytest.mark.requires_vm - def test_startup_time(self, vm_ready, benchmark): - """Benchmark VM startup time with minimal code.""" - import flashvm as rip - - def minimal_execution(): - return rip.run('print("benchmark")') - - result = benchmark(minimal_execution) - - # Verify execution was successful - assert result['exit_code'] == 0 - assert 'benchmark' in result['stdout'] - - # VM startup should be reasonably fast (< 30 seconds) - assert result['execution_time_ms'] < 30000 - - @pytest.mark.benchmark - @pytest.mark.requires_vm - def test_memory_scaling(self, vm_ready): - """Benchmark performance with different memory allocations.""" - import flashvm as rip - import time - - memory_test_code = """ -# Create data structures of various sizes -small_list = list(range(1000)) -medium_list = list(range(10000)) -large_dict = {i: str(i) for i in range(5000)} - -print(f"Small list: {len(small_list)}") -print(f"Medium list: {len(medium_list)}") -print(f"Large dict: {len(large_dict)}") -print("Memory test completed") -""" - - memory_configs = [256, 512, 1024, 2048] - results = {} - - for memory_mb in memory_configs: - start_time = time.time() - result = rip.run(memory_test_code, memory_mb=memory_mb) - end_time = time.time() - - execution_time_ms = (end_time - start_time) * 1000 - results[memory_mb] = execution_time_ms - - # Verify execution succeeded - assert result['exit_code'] == 0 - assert 'Memory test completed' in result['stdout'] - - # Performance should not degrade significantly with more memory - # (within reasonable bounds) - min_time = min(results.values()) - max_time = max(results.values()) - assert max_time / min_time < 3.0 # Max 3x slowdown - - @pytest.mark.benchmark - @pytest.mark.requires_vm - def test_cpu_scaling(self, vm_ready): - """Benchmark performance with different CPU allocations.""" - import flashvm as rip - import time - - cpu_intensive_code = """ -import math - -def cpu_intensive_task(n): - total = 0 - for i in range(n): - total += math.sqrt(i * math.pi) - return total - -# Perform CPU-intensive calculation -result = cpu_intensive_task(50000) -print(f"CPU task result: {result:.2f}") -print("CPU test completed") -""" - - cpu_configs = [1, 2, 4] - results = {} - - for cpus in cpu_configs: - start_time = time.time() - result = rip.run(cpu_intensive_code, cpus=cpus, memory_mb=512) - end_time = time.time() - - execution_time_ms = (end_time - start_time) * 1000 - results[cpus] = execution_time_ms - - # Verify execution succeeded - assert result['exit_code'] == 0 - assert 'CPU test completed' in result['stdout'] - - # More CPUs shouldn't significantly hurt performance for single-threaded tasks - single_cpu_time = results[1] - for cpu_count, exec_time in results.items(): - assert exec_time / single_cpu_time < 2.0 # Max 2x slowdown - - @pytest.mark.benchmark - @pytest.mark.requires_vm - @pytest.mark.slow - def test_concurrent_executions(self, vm_ready): - """Benchmark concurrent VM executions.""" - import flashvm as rip - import threading - import time - - simple_code = """ -print("Task starting") -print("Task completed") -""" - - def single_execution(): - return rip.run(simple_code, timeout_seconds=30) - - # First test sequential execution - start_time = time.time() - sequential_results = [single_execution() for _ in range(2)] - end_time = time.time() - - # Verify sequential results - for result in sequential_results: - assert result['exit_code'] == 0 - assert 'Task completed' in result['stdout'] - - print(f"Sequential execution took {end_time - start_time:.2f} seconds") - - # For concurrent test, we'll just verify that multiple VMs can run - # without going into detailed performance comparison due to resource constraints - def concurrent_executions(count=2): - results = [] - threads = [] - errors = [] - - def worker(): - try: - result = rip.run(simple_code, timeout_seconds=60, memory_mb=256) - results.append(result) - except Exception as e: - errors.append(str(e)) - - start_time = time.time() - - # Start concurrent executions - for _ in range(count): - thread = threading.Thread(target=worker) - threads.append(thread) - thread.start() - time.sleep(0.5) # Small delay between starts - - # Wait for all to complete - for thread in threads: - thread.join() - - end_time = time.time() - - # If we have errors, show them - if errors: - print(f"Errors during concurrent execution: {errors}") - - # Check if we got at least one successful result - successful_results = [r for r in results if r.get('exit_code') == 0] - print(f"Successful concurrent executions: {len(successful_results)} out of {count}") - - # For benchmark purposes, we just need at least one success - assert len(successful_results) >= 1, f"At least one concurrent execution should succeed, got {len(successful_results)}" - - return end_time - start_time - - concurrent_time = concurrent_executions(2) - - # Basic timing comparison - just verify it completes in reasonable time - assert concurrent_time < 120 # Should complete within 2 minutes - - @pytest.mark.benchmark - @pytest.mark.requires_vm - def test_code_size_impact(self, vm_ready): - """Benchmark impact of code size on execution time.""" - import flashvm as rip - import time - - # Generate code of different sizes - def generate_code(size_category): - if size_category == 'small': - return 'print("small code")' - elif size_category == 'medium': - lines = ['print("Line {}")'.format(i) for i in range(20)] # Reduced size - return '\n'.join(lines) - elif size_category == 'large': - lines = ['print("Line {}: {}")'.format(i, i*2) for i in range(100)] # Reduced size - return '\n'.join(lines) - - sizes = ['small', 'medium', 'large'] - results = {} - - for size in sizes: - code = generate_code(size) - - start_time = time.time() - result = rip.run(code, memory_mb=512, timeout_seconds=60) - end_time = time.time() - - execution_time_ms = (end_time - start_time) * 1000 - results[size] = execution_time_ms - - # Verify execution succeeded - print(f"Code size {size} - Exit code: {result['exit_code']}") - if result['exit_code'] != 0: - print(f"stderr: {result.get('stderr', 'N/A')}") - assert result['exit_code'] == 0 - - # Larger code should not cause disproportionate slowdown - small_time = results['small'] - large_time = results['large'] - assert large_time / small_time < 10.0 # Max 10x slowdown - - -class TestResourceUsage: - """Test resource usage patterns.""" - - @pytest.mark.benchmark - @pytest.mark.requires_vm - def test_memory_usage_tracking(self, vm_ready, benchmark): - """Test memory usage tracking and cleanup.""" - import flashvm as rip - - memory_intensive_code = """ -import gc - -# Create and cleanup large data structures -large_data = [] -for i in range(10): - chunk = list(range(10000)) - large_data.append(chunk) - -print(f"Created {len(large_data)} chunks") - -# Cleanup -del large_data -gc.collect() - -print("Memory cleanup completed") -""" - - def memory_test(): - return rip.run(memory_intensive_code, memory_mb=1024, timeout_seconds=30) - - result = benchmark(memory_test) - - # Verify successful execution - assert result['exit_code'] == 0 - assert 'Memory cleanup completed' in result['stdout'] - - # Execution should complete in reasonable time - assert result['execution_time_ms'] < 30000 - - @pytest.mark.benchmark - @pytest.mark.requires_vm - def test_repeated_executions(self, vm_ready): - """Test performance of repeated executions.""" - import flashvm as rip - - simple_code = 'print("repeated execution test")' - - def repeated_execution(): - results = [] - for i in range(5): - result = rip.run(simple_code, memory_mb=256) - results.append(result) - return results - - # Execute multiple rounds for consistency testing - all_results = [] - for round_num in range(3): - round_results = repeated_execution() - all_results.extend(round_results) - - # Verify all executions succeeded - for result in all_results: - assert result['exit_code'] == 0 - assert 'repeated execution test' in result['stdout'] - - # Check for performance consistency - execution_times = [] - for result in all_results: - execution_times.append(result['execution_time_ms']) - - # Performance should be relatively consistent - min_time = min(execution_times) - max_time = max(execution_times) - avg_time = sum(execution_times) / len(execution_times) - - # Variations should be within reasonable bounds - assert max_time / min_time < 5.0 # Max 5x variation - assert abs(max_time - avg_time) / avg_time < 2.0 # Max 200% deviation from average - - -class TestStressTests: - """Stress tests for extreme scenarios.""" - - @pytest.mark.benchmark - @pytest.mark.requires_vm - @pytest.mark.slow - def test_long_running_execution(self, vm_ready, benchmark): - """Test long-running code execution.""" - import flashvm as rip - - long_code = """ -import time - -print("Starting long execution") -for i in range(10): - time.sleep(1) - print(f"Step {i+1}/10 completed") - -print("Long execution completed") -""" - - def long_execution(): - return rip.run(long_code, timeout_seconds=120, memory_mb=512) - - result = benchmark.pedantic( - long_execution, - iterations=1, - rounds=1 - ) - - # Verify successful completion - assert result['exit_code'] == 0 - assert 'Long execution completed' in result['stdout'] - assert result['execution_time_ms'] > 10000 # Should take at least 10 seconds - - @pytest.mark.benchmark - @pytest.mark.requires_vm - @pytest.mark.slow - def test_high_output_volume(self, vm_ready, benchmark): - """Test handling of high-volume output.""" - import flashvm as rip - - high_output_code = """ -# Generate substantial output -for i in range(1000): - print(f"Output line {i}: {'x' * 50}") - -print("High output test completed") -""" - - def high_output_execution(): - return rip.run(high_output_code, memory_mb=512, timeout_seconds=60) - - result = benchmark(high_output_execution) - - # Verify successful execution - assert result['exit_code'] == 0 - assert 'High output test completed' in result['stdout'] - - # Output should be substantial - assert len(result['stdout']) > 50000 # Should have significant output - - # Should complete in reasonable time despite large output - assert result['execution_time_ms'] < 60000 diff --git a/tests/conftest.py b/tests/conftest.py deleted file mode 100644 index bd78ec2..0000000 --- a/tests/conftest.py +++ /dev/null @@ -1,240 +0,0 @@ -""" -Pytest configuration and shared fixtures for flashvm tests. -""" - -import pytest -from typing import Dict, Any - -try: - import flashvm as rip - RIP_AVAILABLE = True -except ImportError: - RIP_AVAILABLE = False - - -@pytest.fixture(scope="session") -def check_rip_available(): - """Check if flashvm module is available.""" - if not RIP_AVAILABLE: - pytest.skip("flashvm module not available") - return True - - -@pytest.fixture(scope="session") -def doctor_check(): - """Check system dependencies using doctor() function.""" - if not RIP_AVAILABLE: - pytest.skip("flashvm module not available") - - try: - deps = rip.doctor() - return deps - except Exception as e: - pytest.skip(f"Doctor check failed: {e}") - - -@pytest.fixture -def vm_ready(doctor_check): - """Check if VM execution is ready.""" - deps = doctor_check - if not deps.get('ready', False): - missing = [] - if not deps.get('krunvm', False): - missing.append('krunvm') - if not deps.get('buildah', False): - missing.append('buildah') - if not deps.get('kvm', False): - missing.append('kvm') - - pytest.skip(f"VM execution not ready. Missing: {', '.join(missing)}") - - return deps - - -@pytest.fixture -def sample_python_code(): - """Simple Python code for testing.""" - return """ -print("Hello from microVM!") -print("Testing basic execution") -result = 2 + 2 -print(f"2 + 2 = {result}") -""" - - -@pytest.fixture -def env_test_code(): - """Python code that tests environment variables.""" - return """ -import os -print("Environment variables test:") -print(f"CUSTOM_VAR = {os.environ.get('CUSTOM_VAR', 'NOT_SET')}") -print(f"TEST_ENV = {os.environ.get('TEST_ENV', 'NOT_SET')}") -print(f"HOME = {os.environ.get('HOME', 'NOT_SET')}") -""" - - -@pytest.fixture -def math_computation_code(): - """Python code that performs mathematical computations.""" - return """ -import math -import statistics - -# Basic math operations -numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] -result = { - 'sum': sum(numbers), - 'mean': statistics.mean(numbers), - 'median': statistics.median(numbers), - 'sqrt_of_100': math.sqrt(100), - 'pi': math.pi -} - -for key, value in result.items(): - print(f"{key}: {value}") -""" - - -@pytest.fixture -def file_operations_code(): - """Python code that performs file operations.""" - return """ -import os -import tempfile - -# Test file operations -test_content = "Hello from microVM file test!" - -# Write to a file -with open('/tmp/test_file.txt', 'w') as f: - f.write(test_content) - -# Read from the file -with open('/tmp/test_file.txt', 'r') as f: - content = f.read() - -print(f"File content: {content}") -print(f"File exists: {os.path.exists('/tmp/test_file.txt')}") - -# List current directory -print("Current directory contents:") -for item in os.listdir('.'): - print(f" {item}") -""" - - -@pytest.fixture -def basic_vm_config(): - """Basic VM configuration for testing.""" - return { - 'cpus': 1, - 'memory_mb': 512, - 'timeout_seconds': 30, - 'network': False, - } - - -@pytest.fixture -def extended_vm_config(): - """Extended VM configuration with environment variables.""" - return { - 'cpus': 2, - 'memory_mb': 1024, - 'timeout_seconds': 60, - 'network': False, - 'env': { - 'CUSTOM_VAR': 'test_value', - 'TEST_ENV': 'pytest_environment', - 'PYTHON_ENV': 'testing' - } - } - - -@pytest.fixture -def performance_config(): - """Configuration optimized for performance testing.""" - return { - 'cpus': 4, - 'memory_mb': 2048, - 'timeout_seconds': 120, - 'network': False, - } - - -class VMExecutionHelper: - """Helper class for VM execution testing.""" - - @staticmethod - def assert_successful_execution(result: Dict[str, Any]): - """Assert that a VM execution was successful.""" - assert 'stdout' in result - assert 'stderr' in result - assert 'exit_code' in result - assert 'execution_time_ms' in result - assert 'image_used' in result - assert result['exit_code'] == 0 - assert isinstance(result['execution_time_ms'], (int, float)) - assert result['execution_time_ms'] > 0 - - @staticmethod - def assert_contains_output(result: Dict[str, Any], expected_text: str): - """Assert that the execution output contains expected text.""" - assert expected_text in result['stdout'] - - @staticmethod - def assert_execution_time_reasonable(result: Dict[str, Any], max_time_ms: int = 30000): - """Assert that execution time is reasonable.""" - exec_time = result['execution_time_ms'] - assert exec_time < max_time_ms, f"Execution took too long: {exec_time}ms" - - -@pytest.fixture -def vm_helper(): - """Fixture providing VM execution helper methods.""" - return VMExecutionHelper() - - -@pytest.fixture -def temp_test_dir(tmp_path): - """Create a temporary directory for test files.""" - test_dir = tmp_path / "rodar_isolado_tests" - test_dir.mkdir() - return test_dir - - -def pytest_configure(config): - """Configure pytest with custom markers.""" - config.addinivalue_line( - "markers", "slow: mark test as slow running" - ) - config.addinivalue_line( - "markers", "integration: mark test as integration test" - ) - config.addinivalue_line( - "markers", "unit: mark test as unit test" - ) - config.addinivalue_line( - "markers", "benchmark: mark test as benchmark test" - ) - config.addinivalue_line( - "markers", "requires_vm: mark test as requiring VM capabilities" - ) - - -def pytest_collection_modifyitems(config, items): - """Automatically mark tests based on their location.""" - for item in items: - # Mark tests in integration directory - if "integration" in str(item.fspath): - item.add_marker(pytest.mark.integration) - item.add_marker(pytest.mark.requires_vm) - - # Mark tests in benchmark directory - if "benchmark" in str(item.fspath): - item.add_marker(pytest.mark.benchmark) - item.add_marker(pytest.mark.slow) - - # Mark tests in unit directory - if "unit" in str(item.fspath): - item.add_marker(pytest.mark.unit) diff --git a/tests/integration/test_complex_scenarios.py b/tests/integration/test_complex_scenarios.py deleted file mode 100644 index fff9338..0000000 --- a/tests/integration/test_complex_scenarios.py +++ /dev/null @@ -1,487 +0,0 @@ -""" -Integration tests for flashvm. - -These tests verify end-to-end functionality including complex scenarios, -file operations, and advanced configurations. -""" - -import pytest - - -class TestComplexExecution: - """Test complex execution scenarios.""" - - @pytest.mark.integration - @pytest.mark.requires_vm - def test_multi_step_computation(self, vm_ready, vm_helper): - """Test execution of multi-step computational task.""" - import flashvm as rip - - complex_code = """ -import math -import json - -# Multi-step computation -def fibonacci(n): - if n <= 1: - return n - return fibonacci(n-1) + fibonacci(n-2) - -def is_prime(n): - if n < 2: - return False - for i in range(2, int(math.sqrt(n)) + 1): - if n % i == 0: - return False - return True - -# Calculate results -fib_numbers = [fibonacci(i) for i in range(10)] -prime_numbers = [i for i in range(2, 50) if is_prime(i)] - -results = { - 'fibonacci': fib_numbers, - 'primes': prime_numbers, - 'stats': { - 'fib_sum': sum(fib_numbers), - 'prime_count': len(prime_numbers), - 'largest_prime': max(prime_numbers) - } -} - -print("=== COMPUTATION RESULTS ===") -print(json.dumps(results, indent=2)) -print("=== END RESULTS ===") -""" - - result = rip.run(complex_code, memory_mb=1024, timeout_seconds=60) - - vm_helper.assert_successful_execution(result) - vm_helper.assert_contains_output(result, "COMPUTATION RESULTS") - vm_helper.assert_contains_output(result, "fibonacci") - vm_helper.assert_contains_output(result, "primes") - - @pytest.mark.integration - @pytest.mark.requires_vm - def test_file_operations(self, vm_ready, vm_helper): - """Test file creation, reading, and manipulation.""" - import flashvm as rip - - file_ops_code = """ -import os -import json -import csv -from pathlib import Path - -# Create various types of files -data_dir = Path("/tmp/test_data") -data_dir.mkdir(exist_ok=True) - -# JSON file -json_data = {"test": "data", "numbers": [1, 2, 3, 4, 5]} -with open(data_dir / "test.json", "w") as f: - json.dump(json_data, f) - -# CSV file -csv_data = [ - ["name", "age", "city"], - ["Alice", 30, "New York"], - ["Bob", 25, "San Francisco"], - ["Charlie", 35, "Chicago"] -] -with open(data_dir / "test.csv", "w", newline="") as f: - writer = csv.writer(f) - writer.writerows(csv_data) - -# Text file -with open(data_dir / "test.txt", "w") as f: - f.write("Hello from file operations test!\\n") - f.write("This is line 2\\n") - f.write("This is line 3\\n") - -# Read and verify files -print("=== FILE OPERATIONS TEST ===") - -# Read JSON -with open(data_dir / "test.json", "r") as f: - json_content = json.load(f) - print(f"JSON content: {json_content}") - -# Read CSV -with open(data_dir / "test.csv", "r") as f: - csv_reader = csv.reader(f) - csv_content = list(csv_reader) - print(f"CSV rows: {len(csv_content)}") - -# Read text file -with open(data_dir / "test.txt", "r") as f: - text_content = f.read() - print(f"Text file length: {len(text_content)} chars") - -# List directory contents -files = list(data_dir.glob("*")) -print(f"Created files: {[f.name for f in files]}") - -print("=== FILE OPERATIONS COMPLETE ===") -""" - - result = rip.run(file_ops_code, memory_mb=512, timeout_seconds=30) - - vm_helper.assert_successful_execution(result) - vm_helper.assert_contains_output(result, "FILE OPERATIONS TEST") - vm_helper.assert_contains_output(result, "JSON content:") - vm_helper.assert_contains_output(result, "CSV rows:") - vm_helper.assert_contains_output(result, "FILE OPERATIONS COMPLETE") - - @pytest.mark.integration - @pytest.mark.requires_vm - def test_python_libraries(self, vm_ready, vm_helper): - """Test execution with standard Python libraries.""" - import flashvm as rip - - library_test_code = """ -import sys -import os -import datetime -import json -import re -import urllib.parse -import base64 -import hashlib -import uuid - -print("=== PYTHON LIBRARIES TEST ===") - -# System info -print(f"Python version: {sys.version}") -print(f"Platform: {sys.platform}") - -# Date/time operations -now = datetime.datetime.now() -print(f"Current time: {now.isoformat()}") - -# JSON operations -data = {"test": True, "value": 42} -json_str = json.dumps(data) -parsed = json.loads(json_str) -print(f"JSON roundtrip: {parsed}") - -# Regular expressions -text = "The quick brown fox jumps over the lazy dog" -matches = re.findall(r"\\b\\w{4}\\b", text) # 4-letter words -print(f"4-letter words: {matches}") - -# URL operations -url = "https://example.com/path?param=value" -parsed_url = urllib.parse.urlparse(url) -print(f"URL host: {parsed_url.netloc}") - -# Base64 encoding -message = "Hello, World!" -encoded = base64.b64encode(message.encode()).decode() -decoded = base64.b64decode(encoded).decode() -print(f"Base64 roundtrip: {decoded}") - -# Hashing -text_hash = hashlib.sha256(message.encode()).hexdigest() -print(f"SHA256 hash: {text_hash[:16]}...") - -# UUID generation -test_uuid = str(uuid.uuid4()) -print(f"Generated UUID: {test_uuid}") - -print("=== LIBRARIES TEST COMPLETE ===") -""" - - result = rip.run(library_test_code, memory_mb=512, timeout_seconds=30) - - vm_helper.assert_successful_execution(result) - vm_helper.assert_contains_output(result, "PYTHON LIBRARIES TEST") - vm_helper.assert_contains_output(result, "Python version:") - vm_helper.assert_contains_output(result, "JSON roundtrip:") - vm_helper.assert_contains_output(result, "LIBRARIES TEST COMPLETE") - - @pytest.mark.integration - @pytest.mark.requires_vm - def test_error_recovery(self, vm_ready, vm_helper): - """Test error handling and recovery in complex scenarios.""" - import flashvm as rip - - error_handling_code = """ -import sys -import traceback - -print("=== ERROR HANDLING TEST ===") - -def test_function_with_error(): - try: - # This will cause a division by zero error - result = 10 / 0 - return result - except ZeroDivisionError as e: - print(f"Caught expected error: {e}") - return "error_handled" - -def test_file_error(): - try: - # Try to read a non-existent file - with open("/nonexistent/file.txt", "r") as f: - return f.read() - except FileNotFoundError as e: - print(f"File error handled: {e}") - return "file_error_handled" - -def test_type_error(): - try: - # Try to add incompatible types - result = "string" + 42 - return result - except TypeError as e: - print(f"Type error handled: {e}") - return "type_error_handled" - -# Test all error scenarios -results = [] -results.append(test_function_with_error()) -results.append(test_file_error()) -results.append(test_type_error()) - -print(f"Error handling results: {results}") - -# Test that normal execution continues -normal_result = 2 + 2 -print(f"Normal computation after errors: {normal_result}") - -print("=== ERROR HANDLING COMPLETE ===") -""" - - result = rip.run(error_handling_code, memory_mb=512, timeout_seconds=30) - - vm_helper.assert_successful_execution(result) - vm_helper.assert_contains_output(result, "ERROR HANDLING TEST") - vm_helper.assert_contains_output(result, "Caught expected error:") - vm_helper.assert_contains_output(result, "ERROR HANDLING COMPLETE") - - -class TestAdvancedConfiguration: - """Test advanced configuration scenarios.""" - - @pytest.mark.integration - @pytest.mark.requires_vm - def test_environment_inheritance(self, vm_ready, vm_helper): - """Test complex environment variable scenarios.""" - import flashvm as rip - - env_code = """ -import os - -print("=== ENVIRONMENT TEST ===") - -# Test multiple environment variables -env_vars = { - 'SIMPLE_VAR': os.environ.get('SIMPLE_VAR', 'NOT_SET'), - 'PATH_VAR': os.environ.get('PATH_VAR', 'NOT_SET'), - 'JSON_VAR': os.environ.get('JSON_VAR', 'NOT_SET'), - 'NUMERIC_VAR': os.environ.get('NUMERIC_VAR', 'NOT_SET'), -} - -for var, value in env_vars.items(): - print(f"{var} = {value}") - -# Test PATH environment variable -path = os.environ.get('PATH', '') -print(f"PATH length: {len(path)}") -print(f"PATH contains /usr/bin: {'/usr/bin' in path}") - -print("=== ENVIRONMENT TEST COMPLETE ===") -""" - - complex_env = { - 'SIMPLE_VAR': 'simple_value', - 'PATH_VAR': '/custom/path:/another/path', - 'JSON_VAR': '{"key": "value", "number": 123}', - 'NUMERIC_VAR': '42', - } - - result = rip.run(env_code, env=complex_env, memory_mb=512) - - vm_helper.assert_successful_execution(result) - vm_helper.assert_contains_output(result, "SIMPLE_VAR = simple_value") - vm_helper.assert_contains_output(result, "JSON_VAR = {\"key\": \"value\"") - vm_helper.assert_contains_output(result, "NUMERIC_VAR = 42") - - @pytest.mark.integration - @pytest.mark.requires_vm - @pytest.mark.slow - def test_memory_intensive_task(self, vm_ready, vm_helper): - """Test memory-intensive computations.""" - import flashvm as rip - - memory_code = """ -import gc - -print("=== MEMORY INTENSIVE TEST ===") - -# Create a large list -large_list = list(range(100000)) -print(f"Created list with {len(large_list)} elements") - -# Perform operations on the list -squared = [x * x for x in large_list] -print(f"Squared list length: {len(squared)}") - -# Create nested structures -nested_data = { - f"key_{i}": [j for j in range(100)] - for i in range(1000) -} -print(f"Created nested structure with {len(nested_data)} keys") - -# Clean up -del large_list -del squared -del nested_data -gc.collect() - -print("Memory cleanup completed") -print("=== MEMORY TEST COMPLETE ===") -""" - - result = rip.run(memory_code, memory_mb=2048, timeout_seconds=60) - - vm_helper.assert_successful_execution(result) - vm_helper.assert_contains_output(result, "MEMORY INTENSIVE TEST") - vm_helper.assert_contains_output(result, "Created list with 100000") - vm_helper.assert_contains_output(result, "MEMORY TEST COMPLETE") - - @pytest.mark.integration - @pytest.mark.requires_vm - def test_config_dict_advanced(self, vm_ready, vm_helper): - """Test advanced configuration dictionary usage.""" - import flashvm as rip - - code = """ -import os - -print("=== ADVANCED CONFIG TEST ===") - -# Check CPU count -cpu_count = os.cpu_count() -print(f"Available CPUs: {cpu_count}") - -# Check memory info (standard library) -def meminfo_mb(): - total = avail = None - try: - with open('/proc/meminfo') as f: - data = f.read().splitlines() - kv = {} - for line in data: - if ':' in line: - k, v = line.split(':', 1) - vnum = v.strip().split()[0] - if vnum.isdigit(): - kv[k] = int(vnum) - total = kv.get('MemTotal') - avail = kv.get('MemAvailable', kv.get('MemFree')) - if total: - print(f"Total memory: {total // 1024} MB") - if avail: - print(f"Available memory: {avail // 1024} MB") - except Exception as e: - print(f"Memory info not available: {e}") - -meminfo_mb() - -# Test environment variables -test_vars = ['TEST_VAR1', 'TEST_VAR2', 'COMPLEX_VAR'] -for var in test_vars: - value = os.environ.get(var, 'NOT_SET') - print(f"{var}: {value}") - -print("=== ADVANCED CONFIG COMPLETE ===") -""" - - advanced_config = { - 'cpus': 2, - 'memory_mb': 1536, - 'timeout_seconds': 45, - 'env': { - 'TEST_VAR1': 'value1', - 'TEST_VAR2': 'value2', - 'COMPLEX_VAR': 'complex_value_with_special_chars_!@#$%', - }, - 'python_args': ['-u', '-O'], - } - - result = rip.run_with_config(code, advanced_config) - - vm_helper.assert_successful_execution(result) - vm_helper.assert_contains_output(result, "ADVANCED CONFIG TEST") - vm_helper.assert_contains_output(result, "TEST_VAR1: value1") - vm_helper.assert_contains_output(result, "TEST_VAR2: value2") - - -class TestImageManagement: - """Test image management functionality.""" - - @pytest.mark.integration - def test_doctor_detailed(self, check_rip_available): - """Test detailed doctor() functionality.""" - import flashvm as rip - - result = rip.doctor() - - # Verify all required fields are present - required_fields = ['krunvm', 'buildah', 'kvm', 'offline_mode', 'ready'] - for field in required_fields: - assert field in result - assert isinstance(result[field], bool) - - # If system is ready, all dependencies should be available - if result['ready']: - assert result['krunvm'] is True - assert result['buildah'] is True - assert result['kvm'] is True - - @pytest.mark.integration - def test_image_functions(self, check_rip_available): - """Test image management functions.""" - import flashvm as rip - - # Test list_cached_images (should not fail even if empty) - try: - cached_images = rip.list_cached_images() - assert isinstance(cached_images, list) - except Exception as e: - # Function might not be fully implemented yet - assert "não implementado" in str(e).lower() or "not implemented" in str(e).lower() - - # Test clear_cache (should not fail) - try: - result = rip.clear_cache() - assert isinstance(result, bool) - except Exception as e: - # Function might not be fully implemented yet - assert "não implementado" in str(e).lower() or "not implemented" in str(e).lower() - - @pytest.mark.integration - def test_prepare_image(self, check_rip_available): - """Test image preparation functionality.""" - import flashvm as rip - - # Test with common image references - test_images = [ - "python:3.11-slim", - "alpine:latest", - ] - - for image in test_images: - try: - result = rip.prepare_image(image) - # Should return boolean - assert isinstance(result, bool) - except Exception as e: - # May fail due to network or system configuration - # This is acceptable for testing - assert isinstance(e, Exception) diff --git a/tests/unit/test_basic_api.py b/tests/unit/test_basic_api.py deleted file mode 100644 index d790a24..0000000 --- a/tests/unit/test_basic_api.py +++ /dev/null @@ -1,301 +0,0 @@ -""" -Unit tests for basic flashvm functionality. - -These tests cover basic API usage, parameter validation, and simple execution scenarios. -""" - -import pytest - - -class TestBasicAPI: - """Test basic API functionality.""" - - def test_module_import(self, check_rip_available): - """Test that the module can be imported.""" - import flashvm as rip - assert hasattr(rip, 'run') - assert hasattr(rip, 'run_with_config') - assert hasattr(rip, 'doctor') - - def test_doctor_function(self, check_rip_available): - """Test the doctor() function returns proper structure.""" - import flashvm as rip - - result = rip.doctor() - - # Check required keys - assert isinstance(result, dict) - assert 'krunvm' in result - assert 'buildah' in result - assert 'kvm' in result - assert 'offline_mode' in result - assert 'ready' in result - - # Check types - assert isinstance(result['krunvm'], bool) - assert isinstance(result['buildah'], bool) - assert isinstance(result['kvm'], bool) - assert isinstance(result['offline_mode'], bool) - assert isinstance(result['ready'], bool) - - def test_run_function_signature(self, check_rip_available): - """Test that run() function has correct signature.""" - import flashvm as rip - import inspect - - sig = inspect.signature(rip.run) - params = list(sig.parameters.keys()) - - # Check that 'code' is the first parameter - assert params[0] == 'code' - - # Check for expected optional parameters - expected_params = [ - 'code', 'image', 'cpus', 'memory_mb', 'env', - 'timeout_seconds', 'workdir', 'python_args', 'network' - ] - - for param in expected_params: - assert param in params - - def test_run_with_config_signature(self, check_rip_available): - """Test that run_with_config() function has correct signature.""" - import flashvm as rip - import inspect - - sig = inspect.signature(rip.run_with_config) - params = list(sig.parameters.keys()) - - assert 'code' in params - assert 'config' in params - - -class TestParameterValidation: - """Test parameter validation and error handling.""" - - def test_empty_code_parameter(self, check_rip_available): - """Test behavior with empty code parameter.""" - import flashvm as rip - - # Empty string should not crash (though execution might fail) - try: - result = rip.run("") - # If it succeeds, check the structure - assert isinstance(result, dict) - except Exception as e: - # If it fails, it should be a reasonable error - assert isinstance(e, Exception) - - def test_invalid_config_types(self, check_rip_available): - """Test behavior with invalid configuration types.""" - import flashvm as rip - - code = "print('test')" - - # Test invalid cpus - may not raise exception, just use default - try: - result = rip.run(code, cpus=-1) - # If it doesn't raise exception, should still return a result - assert isinstance(result, dict) - except Exception: - # Or it might raise an exception, which is also acceptable - pass - - # Test invalid memory - may not raise exception, just use default - try: - result = rip.run(code, memory_mb=0) - assert isinstance(result, dict) - except Exception: - pass - - # Test invalid timeout - may not raise exception, just use default - try: - result = rip.run(code, timeout_seconds=-1) - assert isinstance(result, dict) - except Exception: - pass - - def test_config_dict_structure(self, check_rip_available): - """Test run_with_config with various config structures.""" - import flashvm as rip - - code = "print('test')" - - # Empty config should work - try: - result = rip.run_with_config(code, {}) - assert isinstance(result, dict) - except Exception: - # May fail due to missing dependencies, but shouldn't crash - pass - - # Config with valid parameters - config = { - 'cpus': 1, - 'memory_mb': 512, - 'timeout_seconds': 30 - } - - try: - result = rip.run_with_config(code, config) - assert isinstance(result, dict) - except Exception: - # May fail due to missing dependencies - pass - - -class TestErrorHandling: - """Test error handling scenarios.""" - - def test_syntax_error_code(self, vm_ready): - """Test execution of Python code with syntax errors.""" - import flashvm as rip - - # Python code with syntax error - invalid_code = """ -print("Hello") - invalid indentation -print("World") -""" - - result = rip.run(invalid_code) - - # Should return a result structure even for failed execution - assert isinstance(result, dict) - assert 'exit_code' in result - assert 'stderr' in result - - # Exit code should indicate failure - assert result['exit_code'] != 0 - - # stderr should contain error information - assert len(result['stderr']) > 0 - - def test_runtime_error_code(self, vm_ready): - """Test execution of Python code with runtime errors.""" - import flashvm as rip - - # Python code with runtime error - error_code = """ -print("Starting execution") -x = 1 / 0 # Division by zero -print("This should not print") -""" - - result = rip.run(error_code) - - assert isinstance(result, dict) - assert result['exit_code'] != 0 - assert 'ZeroDivisionError' in result['stderr'] or 'ZeroDivisionError' in result['stdout'] - - @pytest.mark.timeout(10) # Pytest timeout para evitar travamento - def test_timeout_handling(self, vm_ready): - """Test timeout handling for long-running code.""" - import flashvm as rip - - # Code that runs longer than timeout - but not too long for testing - long_running_code = """ -import time -print("Starting long operation") -time.sleep(8) # Sleep for 8 seconds -print("Finished") -""" - - # Set a short timeout (3 seconds) - result = rip.run(long_running_code, timeout_seconds=3) - - assert isinstance(result, dict) - # Should timeout before completion - # Note: timeout behavior may vary depending on implementation - - -class TestBasicExecution: - """Test basic code execution scenarios.""" - - @pytest.mark.requires_vm - def test_simple_print(self, vm_ready, vm_helper): - """Test simple print statement execution.""" - import flashvm as rip - - code = 'print("Hello from microVM!")' - result = rip.run(code) - - vm_helper.assert_successful_execution(result) - vm_helper.assert_contains_output(result, "Hello from microVM!") - - @pytest.mark.requires_vm - def test_basic_math(self, vm_ready, vm_helper, math_computation_code): - """Test basic mathematical operations.""" - import flashvm as rip - - result = rip.run(math_computation_code) - - vm_helper.assert_successful_execution(result) - vm_helper.assert_contains_output(result, "sum: 55") - vm_helper.assert_contains_output(result, "sqrt_of_100: 10.0") - - @pytest.mark.requires_vm - def test_environment_variables(self, vm_ready, vm_helper, env_test_code): - """Test environment variable access.""" - import flashvm as rip - - env_vars = { - 'CUSTOM_VAR': 'test_value_123', - 'TEST_ENV': 'pytest_test' - } - - result = rip.run(env_test_code, env=env_vars) - - vm_helper.assert_successful_execution(result) - vm_helper.assert_contains_output(result, "CUSTOM_VAR = test_value_123") - vm_helper.assert_contains_output(result, "TEST_ENV = pytest_test") - - @pytest.mark.requires_vm - def test_different_memory_sizes(self, vm_ready, vm_helper, sample_python_code): - """Test execution with different memory configurations.""" - import flashvm as rip - - memory_sizes = [256, 512, 1024] - - for memory_mb in memory_sizes: - result = rip.run(sample_python_code, memory_mb=memory_mb) - vm_helper.assert_successful_execution(result) - vm_helper.assert_contains_output(result, "Hello from microVM!") - - @pytest.mark.requires_vm - def test_different_cpu_counts(self, vm_ready, vm_helper, sample_python_code): - """Test execution with different CPU configurations.""" - import flashvm as rip - - cpu_counts = [1, 2] - - for cpus in cpu_counts: - result = rip.run(sample_python_code, cpus=cpus) - vm_helper.assert_successful_execution(result) - vm_helper.assert_contains_output(result, "Hello from microVM!") - - -class TestConfigDictInterface: - """Test the config dictionary interface.""" - - @pytest.mark.requires_vm - def test_basic_config_dict(self, vm_ready, vm_helper, basic_vm_config): - """Test run_with_config with basic configuration.""" - import flashvm as rip - - code = 'print("Config dict test")' - result = rip.run_with_config(code, basic_vm_config) - - vm_helper.assert_successful_execution(result) - vm_helper.assert_contains_output(result, "Config dict test") - - @pytest.mark.requires_vm - def test_extended_config_dict(self, vm_ready, vm_helper, extended_vm_config, env_test_code): - """Test run_with_config with extended configuration including env vars.""" - import flashvm as rip - - result = rip.run_with_config(env_test_code, extended_vm_config) - - vm_helper.assert_successful_execution(result) - vm_helper.assert_contains_output(result, "CUSTOM_VAR = test_value") - vm_helper.assert_contains_output(result, "TEST_ENV = pytest_environment") diff --git a/tests/unit/test_edge_cases.py b/tests/unit/test_edge_cases.py deleted file mode 100644 index ae734cc..0000000 --- a/tests/unit/test_edge_cases.py +++ /dev/null @@ -1,413 +0,0 @@ -""" -Additional utility tests for flashvm. - -Tests for edge cases, error scenarios, and utility functions. -""" - -import pytest - - -class TestEdgeCases: - """Test edge cases and boundary conditions.""" - - @pytest.mark.unit - def test_empty_code_execution(self, check_rip_available): - """Test execution with empty code.""" - import flashvm as rip - - # Empty string - try: - result = rip.run("") - assert isinstance(result, dict) - # May succeed or fail, but should not crash - except Exception: - # May raise exception, which is acceptable - pass - - # Whitespace only - try: - result = rip.run(" \n\t ") - assert isinstance(result, dict) - except Exception: - pass - - @pytest.mark.unit - def test_very_long_code(self, check_rip_available): - """Test execution with very long code strings.""" - import flashvm as rip - - # Generate a very long but valid Python code - long_code_lines = ['print(f"Line {i}")' for i in range(1000)] - long_code = '\n'.join(long_code_lines) - - try: - result = rip.run(long_code, memory_mb=1024, timeout_seconds=120) - if result['exit_code'] == 0: - assert 'Line 999' in result['stdout'] - except Exception as e: - # May fail due to system limitations - assert isinstance(e, Exception) - - @pytest.mark.unit - def test_special_characters_in_code(self, check_rip_available): - """Test execution with special characters.""" - import flashvm as rip - - special_code = ''' -# Test with various special characters -text = "Hello! @#$%^&*()_+-=[]{}|;:'\",./<>?`~" -print(f"Special text: {text}") - -# Unicode characters -unicode_text = "Olá, 世界! 🌍 🐍" -print(f"Unicode: {unicode_text}") - -# Emojis and symbols -symbols = "→ ← ↑ ↓ ★ ♠ ♣ ♥ ♦" -print(f"Symbols: {symbols}") -''' - - try: - result = rip.run(special_code, memory_mb=512) - if result['exit_code'] == 0: - assert 'Special text:' in result['stdout'] - assert 'Unicode:' in result['stdout'] - except Exception: - # May fail due to encoding issues - pass - - @pytest.mark.unit - def test_boundary_resource_values(self, check_rip_available): - """Test boundary values for resource allocation.""" - import flashvm as rip - - simple_code = 'print("boundary test")' - - # Test minimum values - try: - result = rip.run(simple_code, cpus=1, memory_mb=128) - assert isinstance(result, dict) - except Exception: - # May fail if system doesn't support such low values - pass - - # Test higher values (if system supports) - try: - result = rip.run(simple_code, cpus=8, memory_mb=4096, timeout_seconds=10) - assert isinstance(result, dict) - except Exception: - # May fail due to system limitations - pass - - -class TestErrorScenarios: - """Test various error scenarios.""" - - @pytest.mark.unit - def test_invalid_python_syntax(self, vm_ready): - """Test handling of invalid Python syntax.""" - import flashvm as rip - - invalid_codes = [ - "print('unclosed string", - "def broken_function(\npass", - "if True\nprint('missing colon')", - "for i in range(10\nprint(i)", - "import nonexistent_module_xyz_123", - ] - - for invalid_code in invalid_codes: - result = rip.run(invalid_code) - - # Should return error result, not crash - assert isinstance(result, dict) - assert 'exit_code' in result - assert result['exit_code'] != 0 - assert len(result['stderr']) > 0 or 'SyntaxError' in result['stdout'] - - @pytest.mark.unit - def test_runtime_exceptions(self, vm_ready): - """Test handling of runtime exceptions.""" - import flashvm as rip - - exception_codes = [ - # Division by zero - "print(1 / 0)", - - # Index error - "my_list = [1, 2, 3]\nprint(my_list[10])", - - # Key error - "my_dict = {'a': 1}\nprint(my_dict['nonexistent'])", - - # Type error - "print('string' + 42)", - - # Name error - "print(undefined_variable)", - ] - - for exception_code in exception_codes: - result = rip.run(exception_code) - - # Should handle error gracefully - assert isinstance(result, dict) - assert result['exit_code'] != 0 - # Error information should be in stderr or stdout - error_info = result['stderr'] + result['stdout'] - assert len(error_info) > 0 - - @pytest.mark.unit - @pytest.mark.timeout(10) # Pytest timeout para evitar travamento - def test_infinite_loop_timeout(self, vm_ready): - """Test timeout handling for infinite loops.""" - import flashvm as rip - - infinite_loop_code = """ -print("Starting infinite loop") -import time -# Use a loop with sleep to be more gentle on the system -for i in range(1000): - time.sleep(0.01) - if i > 100: # Exit after reasonable time for testing - break -print("Loop finished") -""" - - result = rip.run(infinite_loop_code, timeout_seconds=5) - - # Should complete or timeout gracefully - assert isinstance(result, dict) - # The key is that it should not hang indefinitely - - @pytest.mark.unit - @pytest.mark.timeout(15) # Timeout adicional de segurança - def test_slow_computation_timeout(self, vm_ready): - """Test timeout with slow but finite computation.""" - import flashvm as rip - - slow_code = """ -import time -print("Starting slow computation") -time.sleep(10) # Sleep for 10 seconds -print("Slow computation finished") -""" - - # Set timeout shorter than the sleep time - result = rip.run(slow_code, timeout_seconds=3) - - # Should timeout before completion - assert isinstance(result, dict) - # May have different behaviors depending on timeout implementation - - @pytest.mark.unit - def test_memory_exhaustion(self, vm_ready): - """Test handling of memory exhaustion.""" - import flashvm as rip - - memory_bomb_code = """ -try: - # Try to allocate large amounts of memory - big_list = [] - for i in range(1000000): - big_list.append([0] * 1000) -except MemoryError: - print("MemoryError caught") -except Exception as e: - print(f"Other error: {e}") -""" - - result = rip.run(memory_bomb_code, memory_mb=256, timeout_seconds=30) - - # Should handle memory limits gracefully - assert isinstance(result, dict) - # May succeed with caught MemoryError or fail with system limits - - -class TestConfigurationValidation: - """Test configuration parameter validation.""" - - @pytest.mark.unit - def test_invalid_parameter_types(self, check_rip_available): - """Test handling of invalid parameter types.""" - import flashvm as rip - - simple_code = 'print("test")' - - # Test invalid types for each parameter - invalid_params = [ - {'cpus': 'not_a_number'}, - {'memory_mb': 'not_a_number'}, - {'timeout_seconds': 'not_a_number'}, - {'env': 'not_a_dict'}, - {'python_args': 'not_a_list'}, - {'network': 'not_a_bool'}, - ] - - for invalid_param in invalid_params: - with pytest.raises(Exception): - rip.run(simple_code, **invalid_param) - - @pytest.mark.unit - def test_invalid_parameter_values(self, check_rip_available): - """Test handling of invalid parameter values.""" - import flashvm as rip - - simple_code = 'print("test")' - - # Test invalid values - may not raise exception, just use defaults - invalid_values = [ - {'cpus': -1}, - {'cpus': 0}, - {'memory_mb': -1}, - {'memory_mb': 0}, - {'timeout_seconds': -1}, - ] - - for invalid_value in invalid_values: - try: - result = rip.run(simple_code, **invalid_value) - # If no exception, should still return valid result structure - assert isinstance(result, dict) - except Exception: - # Or might raise exception, which is also acceptable - pass - - @pytest.mark.unit - def test_config_dict_validation(self, check_rip_available): - """Test validation of configuration dictionaries.""" - import flashvm as rip - - simple_code = 'print("test")' - - # Test invalid config dictionary structures - invalid_configs = [ - {'invalid_key': 'value'}, - {'cpus': 'not_a_number'}, - {'env': ['not', 'a', 'dict']}, - ] - - for invalid_config in invalid_configs: - try: - result = rip.run_with_config(simple_code, invalid_config) - # Some invalid configs might be ignored rather than raise errors - assert isinstance(result, dict) - except Exception: - # Or they might raise exceptions, which is also acceptable - pass - - -class TestEnvironmentVariables: - """Test environment variable handling.""" - - @pytest.mark.requires_vm - def test_environment_variable_types(self, vm_ready, vm_helper): - """Test different types of environment variable values.""" - import flashvm as rip - - env_test_code = """ -import os - -vars_to_test = [ - 'STRING_VAR', - 'NUMERIC_VAR', - 'BOOLEAN_VAR', - 'JSON_VAR', - 'PATH_VAR', - 'EMPTY_VAR' -] - -for var in vars_to_test: - value = os.environ.get(var, 'NOT_SET') - print(f"{var}: {repr(value)}") -""" - - complex_env = { - 'STRING_VAR': 'hello world', - 'NUMERIC_VAR': '12345', - 'BOOLEAN_VAR': 'true', - 'JSON_VAR': '{"key": "value", "number": 42}', - 'PATH_VAR': '/usr/bin:/usr/local/bin:/bin', - 'EMPTY_VAR': '', - } - - result = rip.run(env_test_code, env=complex_env) - - vm_helper.assert_successful_execution(result) - - # Verify all variables are set correctly - for var_name, expected_value in complex_env.items(): - assert f"{var_name}: '{expected_value}'" in result['stdout'] - - @pytest.mark.requires_vm - def test_environment_variable_special_chars(self, vm_ready, vm_helper): - """Test environment variables with special characters.""" - import flashvm as rip - - special_env_code = """ -import os - -special_vars = ['SPECIAL_CHARS', 'UNICODE_VAR', 'SYMBOLS_VAR'] -for var in special_vars: - value = os.environ.get(var, 'NOT_SET') - print(f"{var}: {value}") -""" - - special_env = { - 'SPECIAL_CHARS': '!@#$%^&*()_+-=[]{}|;:\'",.<>?`~', - 'UNICODE_VAR': 'Olá, 世界! 🌍', - 'SYMBOLS_VAR': '→←↑↓★♠♣♥♦', - } - - try: - result = rip.run(special_env_code, env=special_env) - if result['exit_code'] == 0: - # Verify at least some special characters are preserved - assert 'SPECIAL_CHARS:' in result['stdout'] - except Exception: - # May fail due to encoding or shell escaping issues - pass - - -class TestImageHandling: - """Test image specification and handling.""" - - @pytest.mark.unit - def test_default_image(self, check_rip_available): - """Test execution with default image (None).""" - import flashvm as rip - - simple_code = 'print("default image test")' - - try: - result = rip.run(simple_code) - assert isinstance(result, dict) - assert 'image_used' in result - except Exception: - # May fail if no default image is available - pass - - @pytest.mark.unit - def test_explicit_image_specification(self, check_rip_available): - """Test execution with explicitly specified images.""" - import flashvm as rip - - simple_code = 'print("explicit image test")' - - # Test with common image references - test_images = [ - "python:3.11-slim", - "python:3.10", - "alpine:latest", - ] - - for image in test_images: - try: - result = rip.run(simple_code, image=image) - assert isinstance(result, dict) - if 'image_used' in result: - # Image reference should be reflected in result - assert len(result['image_used']) > 0 - except Exception: - # May fail due to image availability or network issues - pass diff --git a/tests/unit/test_wheel_resources.py b/tests/unit/test_wheel_resources.py deleted file mode 100644 index df485e0..0000000 --- a/tests/unit/test_wheel_resources.py +++ /dev/null @@ -1,14 +0,0 @@ -def test_embedded_oci_layout_check_imports(): - import flashvm as rip - info = rip.doctor() - assert 'offline_mode' in info - - -def test_embedded_oci_path_and_message_keys(): - import flashvm as rip - info = rip.doctor() - # Always returns keys; message optional - assert 'krunvm' in info and 'buildah' in info and 'kvm' in info - assert 'offline_mode' in info - # offline_message may or may not exist - assert isinstance(info.get('offline_mode'), bool) diff --git a/uv.lock b/uv.lock index c520b51..491daea 100644 --- a/uv.lock +++ b/uv.lock @@ -1,688 +1,8 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.8" -resolution-markers = [ - "python_full_version >= '3.9'", - "python_full_version < '3.9'", -] - -[[package]] -name = "attrs" -version = "25.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" }, -] - -[[package]] -name = "backports-asyncio-runner" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, -] - -[[package]] -name = "coverage" -version = "7.6.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.9'", -] -sdist = { url = "https://files.pythonhosted.org/packages/f7/08/7e37f82e4d1aead42a7443ff06a1e406aabf7302c4f00a546e4b320b994c/coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d", size = 798791, upload-time = "2024-08-04T19:45:30.9Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/61/eb7ce5ed62bacf21beca4937a90fe32545c91a3c8a42a30c6616d48fc70d/coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16", size = 206690, upload-time = "2024-08-04T19:43:07.695Z" }, - { url = "https://files.pythonhosted.org/packages/7d/73/041928e434442bd3afde5584bdc3f932fb4562b1597629f537387cec6f3d/coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36", size = 207127, upload-time = "2024-08-04T19:43:10.15Z" }, - { url = "https://files.pythonhosted.org/packages/c7/c8/6ca52b5147828e45ad0242388477fdb90df2c6cbb9a441701a12b3c71bc8/coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02", size = 235654, upload-time = "2024-08-04T19:43:12.405Z" }, - { url = "https://files.pythonhosted.org/packages/d5/da/9ac2b62557f4340270942011d6efeab9833648380109e897d48ab7c1035d/coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc", size = 233598, upload-time = "2024-08-04T19:43:14.078Z" }, - { url = "https://files.pythonhosted.org/packages/53/23/9e2c114d0178abc42b6d8d5281f651a8e6519abfa0ef460a00a91f80879d/coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23", size = 234732, upload-time = "2024-08-04T19:43:16.632Z" }, - { url = "https://files.pythonhosted.org/packages/0f/7e/a0230756fb133343a52716e8b855045f13342b70e48e8ad41d8a0d60ab98/coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34", size = 233816, upload-time = "2024-08-04T19:43:19.049Z" }, - { url = "https://files.pythonhosted.org/packages/28/7c/3753c8b40d232b1e5eeaed798c875537cf3cb183fb5041017c1fdb7ec14e/coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c", size = 232325, upload-time = "2024-08-04T19:43:21.246Z" }, - { url = "https://files.pythonhosted.org/packages/57/e3/818a2b2af5b7573b4b82cf3e9f137ab158c90ea750a8f053716a32f20f06/coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959", size = 233418, upload-time = "2024-08-04T19:43:22.945Z" }, - { url = "https://files.pythonhosted.org/packages/c8/fb/4532b0b0cefb3f06d201648715e03b0feb822907edab3935112b61b885e2/coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232", size = 209343, upload-time = "2024-08-04T19:43:25.121Z" }, - { url = "https://files.pythonhosted.org/packages/5a/25/af337cc7421eca1c187cc9c315f0a755d48e755d2853715bfe8c418a45fa/coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0", size = 210136, upload-time = "2024-08-04T19:43:26.851Z" }, - { url = "https://files.pythonhosted.org/packages/ad/5f/67af7d60d7e8ce61a4e2ddcd1bd5fb787180c8d0ae0fbd073f903b3dd95d/coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93", size = 206796, upload-time = "2024-08-04T19:43:29.115Z" }, - { url = "https://files.pythonhosted.org/packages/e1/0e/e52332389e057daa2e03be1fbfef25bb4d626b37d12ed42ae6281d0a274c/coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3", size = 207244, upload-time = "2024-08-04T19:43:31.285Z" }, - { url = "https://files.pythonhosted.org/packages/aa/cd/766b45fb6e090f20f8927d9c7cb34237d41c73a939358bc881883fd3a40d/coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff", size = 239279, upload-time = "2024-08-04T19:43:33.581Z" }, - { url = "https://files.pythonhosted.org/packages/70/6c/a9ccd6fe50ddaf13442a1e2dd519ca805cbe0f1fcd377fba6d8339b98ccb/coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d", size = 236859, upload-time = "2024-08-04T19:43:35.301Z" }, - { url = "https://files.pythonhosted.org/packages/14/6f/8351b465febb4dbc1ca9929505202db909c5a635c6fdf33e089bbc3d7d85/coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6", size = 238549, upload-time = "2024-08-04T19:43:37.578Z" }, - { url = "https://files.pythonhosted.org/packages/68/3c/289b81fa18ad72138e6d78c4c11a82b5378a312c0e467e2f6b495c260907/coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56", size = 237477, upload-time = "2024-08-04T19:43:39.92Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1c/aa1efa6459d822bd72c4abc0b9418cf268de3f60eeccd65dc4988553bd8d/coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234", size = 236134, upload-time = "2024-08-04T19:43:41.453Z" }, - { url = "https://files.pythonhosted.org/packages/fb/c8/521c698f2d2796565fe9c789c2ee1ccdae610b3aa20b9b2ef980cc253640/coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133", size = 236910, upload-time = "2024-08-04T19:43:43.037Z" }, - { url = "https://files.pythonhosted.org/packages/7d/30/033e663399ff17dca90d793ee8a2ea2890e7fdf085da58d82468b4220bf7/coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c", size = 209348, upload-time = "2024-08-04T19:43:44.787Z" }, - { url = "https://files.pythonhosted.org/packages/20/05/0d1ccbb52727ccdadaa3ff37e4d2dc1cd4d47f0c3df9eb58d9ec8508ca88/coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6", size = 210230, upload-time = "2024-08-04T19:43:46.707Z" }, - { url = "https://files.pythonhosted.org/packages/7e/d4/300fc921dff243cd518c7db3a4c614b7e4b2431b0d1145c1e274fd99bd70/coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778", size = 206983, upload-time = "2024-08-04T19:43:49.082Z" }, - { url = "https://files.pythonhosted.org/packages/e1/ab/6bf00de5327ecb8db205f9ae596885417a31535eeda6e7b99463108782e1/coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391", size = 207221, upload-time = "2024-08-04T19:43:52.15Z" }, - { url = "https://files.pythonhosted.org/packages/92/8f/2ead05e735022d1a7f3a0a683ac7f737de14850395a826192f0288703472/coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8", size = 240342, upload-time = "2024-08-04T19:43:53.746Z" }, - { url = "https://files.pythonhosted.org/packages/0f/ef/94043e478201ffa85b8ae2d2c79b4081e5a1b73438aafafccf3e9bafb6b5/coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d", size = 237371, upload-time = "2024-08-04T19:43:55.993Z" }, - { url = "https://files.pythonhosted.org/packages/1f/0f/c890339dd605f3ebc269543247bdd43b703cce6825b5ed42ff5f2d6122c7/coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca", size = 239455, upload-time = "2024-08-04T19:43:57.618Z" }, - { url = "https://files.pythonhosted.org/packages/d1/04/7fd7b39ec7372a04efb0f70c70e35857a99b6a9188b5205efb4c77d6a57a/coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163", size = 238924, upload-time = "2024-08-04T19:44:00.012Z" }, - { url = "https://files.pythonhosted.org/packages/ed/bf/73ce346a9d32a09cf369f14d2a06651329c984e106f5992c89579d25b27e/coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a", size = 237252, upload-time = "2024-08-04T19:44:01.713Z" }, - { url = "https://files.pythonhosted.org/packages/86/74/1dc7a20969725e917b1e07fe71a955eb34bc606b938316bcc799f228374b/coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d", size = 238897, upload-time = "2024-08-04T19:44:03.898Z" }, - { url = "https://files.pythonhosted.org/packages/b6/e9/d9cc3deceb361c491b81005c668578b0dfa51eed02cd081620e9a62f24ec/coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5", size = 209606, upload-time = "2024-08-04T19:44:05.532Z" }, - { url = "https://files.pythonhosted.org/packages/47/c8/5a2e41922ea6740f77d555c4d47544acd7dc3f251fe14199c09c0f5958d3/coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb", size = 210373, upload-time = "2024-08-04T19:44:07.079Z" }, - { url = "https://files.pythonhosted.org/packages/8c/f9/9aa4dfb751cb01c949c990d136a0f92027fbcc5781c6e921df1cb1563f20/coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106", size = 207007, upload-time = "2024-08-04T19:44:09.453Z" }, - { url = "https://files.pythonhosted.org/packages/b9/67/e1413d5a8591622a46dd04ff80873b04c849268831ed5c304c16433e7e30/coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9", size = 207269, upload-time = "2024-08-04T19:44:11.045Z" }, - { url = "https://files.pythonhosted.org/packages/14/5b/9dec847b305e44a5634d0fb8498d135ab1d88330482b74065fcec0622224/coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c", size = 239886, upload-time = "2024-08-04T19:44:12.83Z" }, - { url = "https://files.pythonhosted.org/packages/7b/b7/35760a67c168e29f454928f51f970342d23cf75a2bb0323e0f07334c85f3/coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a", size = 237037, upload-time = "2024-08-04T19:44:15.393Z" }, - { url = "https://files.pythonhosted.org/packages/f7/95/d2fd31f1d638df806cae59d7daea5abf2b15b5234016a5ebb502c2f3f7ee/coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060", size = 239038, upload-time = "2024-08-04T19:44:17.466Z" }, - { url = "https://files.pythonhosted.org/packages/6e/bd/110689ff5752b67924efd5e2aedf5190cbbe245fc81b8dec1abaffba619d/coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862", size = 238690, upload-time = "2024-08-04T19:44:19.336Z" }, - { url = "https://files.pythonhosted.org/packages/d3/a8/08d7b38e6ff8df52331c83130d0ab92d9c9a8b5462f9e99c9f051a4ae206/coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388", size = 236765, upload-time = "2024-08-04T19:44:20.994Z" }, - { url = "https://files.pythonhosted.org/packages/d6/6a/9cf96839d3147d55ae713eb2d877f4d777e7dc5ba2bce227167d0118dfe8/coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155", size = 238611, upload-time = "2024-08-04T19:44:22.616Z" }, - { url = "https://files.pythonhosted.org/packages/74/e4/7ff20d6a0b59eeaab40b3140a71e38cf52547ba21dbcf1d79c5a32bba61b/coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a", size = 209671, upload-time = "2024-08-04T19:44:24.418Z" }, - { url = "https://files.pythonhosted.org/packages/35/59/1812f08a85b57c9fdb6d0b383d779e47b6f643bc278ed682859512517e83/coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129", size = 210368, upload-time = "2024-08-04T19:44:26.276Z" }, - { url = "https://files.pythonhosted.org/packages/9c/15/08913be1c59d7562a3e39fce20661a98c0a3f59d5754312899acc6cb8a2d/coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e", size = 207758, upload-time = "2024-08-04T19:44:29.028Z" }, - { url = "https://files.pythonhosted.org/packages/c4/ae/b5d58dff26cade02ada6ca612a76447acd69dccdbb3a478e9e088eb3d4b9/coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962", size = 208035, upload-time = "2024-08-04T19:44:30.673Z" }, - { url = "https://files.pythonhosted.org/packages/b8/d7/62095e355ec0613b08dfb19206ce3033a0eedb6f4a67af5ed267a8800642/coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb", size = 250839, upload-time = "2024-08-04T19:44:32.412Z" }, - { url = "https://files.pythonhosted.org/packages/7c/1e/c2967cb7991b112ba3766df0d9c21de46b476d103e32bb401b1b2adf3380/coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704", size = 246569, upload-time = "2024-08-04T19:44:34.547Z" }, - { url = "https://files.pythonhosted.org/packages/8b/61/a7a6a55dd266007ed3b1df7a3386a0d760d014542d72f7c2c6938483b7bd/coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b", size = 248927, upload-time = "2024-08-04T19:44:36.313Z" }, - { url = "https://files.pythonhosted.org/packages/c8/fa/13a6f56d72b429f56ef612eb3bc5ce1b75b7ee12864b3bd12526ab794847/coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f", size = 248401, upload-time = "2024-08-04T19:44:38.155Z" }, - { url = "https://files.pythonhosted.org/packages/75/06/0429c652aa0fb761fc60e8c6b291338c9173c6aa0f4e40e1902345b42830/coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223", size = 246301, upload-time = "2024-08-04T19:44:39.883Z" }, - { url = "https://files.pythonhosted.org/packages/52/76/1766bb8b803a88f93c3a2d07e30ffa359467810e5cbc68e375ebe6906efb/coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3", size = 247598, upload-time = "2024-08-04T19:44:41.59Z" }, - { url = "https://files.pythonhosted.org/packages/66/8b/f54f8db2ae17188be9566e8166ac6df105c1c611e25da755738025708d54/coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f", size = 210307, upload-time = "2024-08-04T19:44:43.301Z" }, - { url = "https://files.pythonhosted.org/packages/9f/b0/e0dca6da9170aefc07515cce067b97178cefafb512d00a87a1c717d2efd5/coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657", size = 211453, upload-time = "2024-08-04T19:44:45.677Z" }, - { url = "https://files.pythonhosted.org/packages/81/d0/d9e3d554e38beea5a2e22178ddb16587dbcbe9a1ef3211f55733924bf7fa/coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0", size = 206674, upload-time = "2024-08-04T19:44:47.694Z" }, - { url = "https://files.pythonhosted.org/packages/38/ea/cab2dc248d9f45b2b7f9f1f596a4d75a435cb364437c61b51d2eb33ceb0e/coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a", size = 207101, upload-time = "2024-08-04T19:44:49.32Z" }, - { url = "https://files.pythonhosted.org/packages/ca/6f/f82f9a500c7c5722368978a5390c418d2a4d083ef955309a8748ecaa8920/coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b", size = 236554, upload-time = "2024-08-04T19:44:51.631Z" }, - { url = "https://files.pythonhosted.org/packages/a6/94/d3055aa33d4e7e733d8fa309d9adf147b4b06a82c1346366fc15a2b1d5fa/coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3", size = 234440, upload-time = "2024-08-04T19:44:53.464Z" }, - { url = "https://files.pythonhosted.org/packages/e4/6e/885bcd787d9dd674de4a7d8ec83faf729534c63d05d51d45d4fa168f7102/coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de", size = 235889, upload-time = "2024-08-04T19:44:55.165Z" }, - { url = "https://files.pythonhosted.org/packages/f4/63/df50120a7744492710854860783d6819ff23e482dee15462c9a833cc428a/coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6", size = 235142, upload-time = "2024-08-04T19:44:57.269Z" }, - { url = "https://files.pythonhosted.org/packages/3a/5d/9d0acfcded2b3e9ce1c7923ca52ccc00c78a74e112fc2aee661125b7843b/coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569", size = 233805, upload-time = "2024-08-04T19:44:59.033Z" }, - { url = "https://files.pythonhosted.org/packages/c4/56/50abf070cb3cd9b1dd32f2c88f083aab561ecbffbcd783275cb51c17f11d/coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989", size = 234655, upload-time = "2024-08-04T19:45:01.398Z" }, - { url = "https://files.pythonhosted.org/packages/25/ee/b4c246048b8485f85a2426ef4abab88e48c6e80c74e964bea5cd4cd4b115/coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7", size = 209296, upload-time = "2024-08-04T19:45:03.819Z" }, - { url = "https://files.pythonhosted.org/packages/5c/1c/96cf86b70b69ea2b12924cdf7cabb8ad10e6130eab8d767a1099fbd2a44f/coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8", size = 210137, upload-time = "2024-08-04T19:45:06.25Z" }, - { url = "https://files.pythonhosted.org/packages/19/d3/d54c5aa83268779d54c86deb39c1c4566e5d45c155369ca152765f8db413/coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255", size = 206688, upload-time = "2024-08-04T19:45:08.358Z" }, - { url = "https://files.pythonhosted.org/packages/a5/fe/137d5dca72e4a258b1bc17bb04f2e0196898fe495843402ce826a7419fe3/coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8", size = 207120, upload-time = "2024-08-04T19:45:11.526Z" }, - { url = "https://files.pythonhosted.org/packages/78/5b/a0a796983f3201ff5485323b225d7c8b74ce30c11f456017e23d8e8d1945/coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2", size = 235249, upload-time = "2024-08-04T19:45:13.202Z" }, - { url = "https://files.pythonhosted.org/packages/4e/e1/76089d6a5ef9d68f018f65411fcdaaeb0141b504587b901d74e8587606ad/coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a", size = 233237, upload-time = "2024-08-04T19:45:14.961Z" }, - { url = "https://files.pythonhosted.org/packages/9a/6f/eef79b779a540326fee9520e5542a8b428cc3bfa8b7c8f1022c1ee4fc66c/coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc", size = 234311, upload-time = "2024-08-04T19:45:16.924Z" }, - { url = "https://files.pythonhosted.org/packages/75/e1/656d65fb126c29a494ef964005702b012f3498db1a30dd562958e85a4049/coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004", size = 233453, upload-time = "2024-08-04T19:45:18.672Z" }, - { url = "https://files.pythonhosted.org/packages/68/6a/45f108f137941a4a1238c85f28fd9d048cc46b5466d6b8dda3aba1bb9d4f/coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb", size = 231958, upload-time = "2024-08-04T19:45:20.63Z" }, - { url = "https://files.pythonhosted.org/packages/9b/e7/47b809099168b8b8c72ae311efc3e88c8d8a1162b3ba4b8da3cfcdb85743/coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36", size = 232938, upload-time = "2024-08-04T19:45:23.062Z" }, - { url = "https://files.pythonhosted.org/packages/52/80/052222ba7058071f905435bad0ba392cc12006380731c37afaf3fe749b88/coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c", size = 209352, upload-time = "2024-08-04T19:45:25.042Z" }, - { url = "https://files.pythonhosted.org/packages/b8/d8/1b92e0b3adcf384e98770a00ca095da1b5f7b483e6563ae4eb5e935d24a1/coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca", size = 210153, upload-time = "2024-08-04T19:45:27.079Z" }, - { url = "https://files.pythonhosted.org/packages/a5/2b/0354ed096bca64dc8e32a7cbcae28b34cb5ad0b1fe2125d6d99583313ac0/coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df", size = 198926, upload-time = "2024-08-04T19:45:28.875Z" }, -] - -[package.optional-dependencies] -toml = [ - { name = "tomli", marker = "python_full_version < '3.9'" }, -] - -[[package]] -name = "coverage" -version = "7.10.6" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.9'", -] -sdist = { url = "https://files.pythonhosted.org/packages/14/70/025b179c993f019105b79575ac6edb5e084fb0f0e63f15cdebef4e454fb5/coverage-7.10.6.tar.gz", hash = "sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90", size = 823736, upload-time = "2025-08-29T15:35:16.668Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/1d/2e64b43d978b5bd184e0756a41415597dfef30fcbd90b747474bd749d45f/coverage-7.10.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:70e7bfbd57126b5554aa482691145f798d7df77489a177a6bef80de78860a356", size = 217025, upload-time = "2025-08-29T15:32:57.169Z" }, - { url = "https://files.pythonhosted.org/packages/23/62/b1e0f513417c02cc10ef735c3ee5186df55f190f70498b3702d516aad06f/coverage-7.10.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e41be6f0f19da64af13403e52f2dec38bbc2937af54df8ecef10850ff8d35301", size = 217419, upload-time = "2025-08-29T15:32:59.908Z" }, - { url = "https://files.pythonhosted.org/packages/e7/16/b800640b7a43e7c538429e4d7223e0a94fd72453a1a048f70bf766f12e96/coverage-7.10.6-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c61fc91ab80b23f5fddbee342d19662f3d3328173229caded831aa0bd7595460", size = 244180, upload-time = "2025-08-29T15:33:01.608Z" }, - { url = "https://files.pythonhosted.org/packages/fb/6f/5e03631c3305cad187eaf76af0b559fff88af9a0b0c180d006fb02413d7a/coverage-7.10.6-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10356fdd33a7cc06e8051413140bbdc6f972137508a3572e3f59f805cd2832fd", size = 245992, upload-time = "2025-08-29T15:33:03.239Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a1/f30ea0fb400b080730125b490771ec62b3375789f90af0bb68bfb8a921d7/coverage-7.10.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:80b1695cf7c5ebe7b44bf2521221b9bb8cdf69b1f24231149a7e3eb1ae5fa2fb", size = 247851, upload-time = "2025-08-29T15:33:04.603Z" }, - { url = "https://files.pythonhosted.org/packages/02/8e/cfa8fee8e8ef9a6bb76c7bef039f3302f44e615d2194161a21d3d83ac2e9/coverage-7.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2e4c33e6378b9d52d3454bd08847a8651f4ed23ddbb4a0520227bd346382bbc6", size = 245891, upload-time = "2025-08-29T15:33:06.176Z" }, - { url = "https://files.pythonhosted.org/packages/93/a9/51be09b75c55c4f6c16d8d73a6a1d46ad764acca0eab48fa2ffaef5958fe/coverage-7.10.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c8a3ec16e34ef980a46f60dc6ad86ec60f763c3f2fa0db6d261e6e754f72e945", size = 243909, upload-time = "2025-08-29T15:33:07.74Z" }, - { url = "https://files.pythonhosted.org/packages/e9/a6/ba188b376529ce36483b2d585ca7bdac64aacbe5aa10da5978029a9c94db/coverage-7.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7d79dabc0a56f5af990cc6da9ad1e40766e82773c075f09cc571e2076fef882e", size = 244786, upload-time = "2025-08-29T15:33:08.965Z" }, - { url = "https://files.pythonhosted.org/packages/d0/4c/37ed872374a21813e0d3215256180c9a382c3f5ced6f2e5da0102fc2fd3e/coverage-7.10.6-cp310-cp310-win32.whl", hash = "sha256:86b9b59f2b16e981906e9d6383eb6446d5b46c278460ae2c36487667717eccf1", size = 219521, upload-time = "2025-08-29T15:33:10.599Z" }, - { url = "https://files.pythonhosted.org/packages/8e/36/9311352fdc551dec5b973b61f4e453227ce482985a9368305880af4f85dd/coverage-7.10.6-cp310-cp310-win_amd64.whl", hash = "sha256:e132b9152749bd33534e5bd8565c7576f135f157b4029b975e15ee184325f528", size = 220417, upload-time = "2025-08-29T15:33:11.907Z" }, - { url = "https://files.pythonhosted.org/packages/d4/16/2bea27e212c4980753d6d563a0803c150edeaaddb0771a50d2afc410a261/coverage-7.10.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c706db3cabb7ceef779de68270150665e710b46d56372455cd741184f3868d8f", size = 217129, upload-time = "2025-08-29T15:33:13.575Z" }, - { url = "https://files.pythonhosted.org/packages/2a/51/e7159e068831ab37e31aac0969d47b8c5ee25b7d307b51e310ec34869315/coverage-7.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e0c38dc289e0508ef68ec95834cb5d2e96fdbe792eaccaa1bccac3966bbadcc", size = 217532, upload-time = "2025-08-29T15:33:14.872Z" }, - { url = "https://files.pythonhosted.org/packages/e7/c0/246ccbea53d6099325d25cd208df94ea435cd55f0db38099dd721efc7a1f/coverage-7.10.6-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:752a3005a1ded28f2f3a6e8787e24f28d6abe176ca64677bcd8d53d6fe2ec08a", size = 247931, upload-time = "2025-08-29T15:33:16.142Z" }, - { url = "https://files.pythonhosted.org/packages/7d/fb/7435ef8ab9b2594a6e3f58505cc30e98ae8b33265d844007737946c59389/coverage-7.10.6-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:689920ecfd60f992cafca4f5477d55720466ad2c7fa29bb56ac8d44a1ac2b47a", size = 249864, upload-time = "2025-08-29T15:33:17.434Z" }, - { url = "https://files.pythonhosted.org/packages/51/f8/d9d64e8da7bcddb094d511154824038833c81e3a039020a9d6539bf303e9/coverage-7.10.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec98435796d2624d6905820a42f82149ee9fc4f2d45c2c5bc5a44481cc50db62", size = 251969, upload-time = "2025-08-29T15:33:18.822Z" }, - { url = "https://files.pythonhosted.org/packages/43/28/c43ba0ef19f446d6463c751315140d8f2a521e04c3e79e5c5fe211bfa430/coverage-7.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b37201ce4a458c7a758ecc4efa92fa8ed783c66e0fa3c42ae19fc454a0792153", size = 249659, upload-time = "2025-08-29T15:33:20.407Z" }, - { url = "https://files.pythonhosted.org/packages/79/3e/53635bd0b72beaacf265784508a0b386defc9ab7fad99ff95f79ce9db555/coverage-7.10.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2904271c80898663c810a6b067920a61dd8d38341244a3605bd31ab55250dad5", size = 247714, upload-time = "2025-08-29T15:33:21.751Z" }, - { url = "https://files.pythonhosted.org/packages/4c/55/0964aa87126624e8c159e32b0bc4e84edef78c89a1a4b924d28dd8265625/coverage-7.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5aea98383463d6e1fa4e95416d8de66f2d0cb588774ee20ae1b28df826bcb619", size = 248351, upload-time = "2025-08-29T15:33:23.105Z" }, - { url = "https://files.pythonhosted.org/packages/eb/ab/6cfa9dc518c6c8e14a691c54e53a9433ba67336c760607e299bfcf520cb1/coverage-7.10.6-cp311-cp311-win32.whl", hash = "sha256:e3fb1fa01d3598002777dd259c0c2e6d9d5e10e7222976fc8e03992f972a2cba", size = 219562, upload-time = "2025-08-29T15:33:24.717Z" }, - { url = "https://files.pythonhosted.org/packages/5b/18/99b25346690cbc55922e7cfef06d755d4abee803ef335baff0014268eff4/coverage-7.10.6-cp311-cp311-win_amd64.whl", hash = "sha256:f35ed9d945bece26553d5b4c8630453169672bea0050a564456eb88bdffd927e", size = 220453, upload-time = "2025-08-29T15:33:26.482Z" }, - { url = "https://files.pythonhosted.org/packages/d8/ed/81d86648a07ccb124a5cf1f1a7788712b8d7216b593562683cd5c9b0d2c1/coverage-7.10.6-cp311-cp311-win_arm64.whl", hash = "sha256:99e1a305c7765631d74b98bf7dbf54eeea931f975e80f115437d23848ee8c27c", size = 219127, upload-time = "2025-08-29T15:33:27.777Z" }, - { url = "https://files.pythonhosted.org/packages/26/06/263f3305c97ad78aab066d116b52250dd316e74fcc20c197b61e07eb391a/coverage-7.10.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5b2dd6059938063a2c9fee1af729d4f2af28fd1a545e9b7652861f0d752ebcea", size = 217324, upload-time = "2025-08-29T15:33:29.06Z" }, - { url = "https://files.pythonhosted.org/packages/e9/60/1e1ded9a4fe80d843d7d53b3e395c1db3ff32d6c301e501f393b2e6c1c1f/coverage-7.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:388d80e56191bf846c485c14ae2bc8898aa3124d9d35903fef7d907780477634", size = 217560, upload-time = "2025-08-29T15:33:30.748Z" }, - { url = "https://files.pythonhosted.org/packages/b8/25/52136173c14e26dfed8b106ed725811bb53c30b896d04d28d74cb64318b3/coverage-7.10.6-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:90cb5b1a4670662719591aa92d0095bb41714970c0b065b02a2610172dbf0af6", size = 249053, upload-time = "2025-08-29T15:33:32.041Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1d/ae25a7dc58fcce8b172d42ffe5313fc267afe61c97fa872b80ee72d9515a/coverage-7.10.6-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:961834e2f2b863a0e14260a9a273aff07ff7818ab6e66d2addf5628590c628f9", size = 251802, upload-time = "2025-08-29T15:33:33.625Z" }, - { url = "https://files.pythonhosted.org/packages/f5/7a/1f561d47743710fe996957ed7c124b421320f150f1d38523d8d9102d3e2a/coverage-7.10.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf9a19f5012dab774628491659646335b1928cfc931bf8d97b0d5918dd58033c", size = 252935, upload-time = "2025-08-29T15:33:34.909Z" }, - { url = "https://files.pythonhosted.org/packages/6c/ad/8b97cd5d28aecdfde792dcbf646bac141167a5cacae2cd775998b45fabb5/coverage-7.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:99c4283e2a0e147b9c9cc6bc9c96124de9419d6044837e9799763a0e29a7321a", size = 250855, upload-time = "2025-08-29T15:33:36.922Z" }, - { url = "https://files.pythonhosted.org/packages/33/6a/95c32b558d9a61858ff9d79580d3877df3eb5bc9eed0941b1f187c89e143/coverage-7.10.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:282b1b20f45df57cc508c1e033403f02283adfb67d4c9c35a90281d81e5c52c5", size = 248974, upload-time = "2025-08-29T15:33:38.175Z" }, - { url = "https://files.pythonhosted.org/packages/0d/9c/8ce95dee640a38e760d5b747c10913e7a06554704d60b41e73fdea6a1ffd/coverage-7.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cdbe264f11afd69841bd8c0d83ca10b5b32853263ee62e6ac6a0ab63895f972", size = 250409, upload-time = "2025-08-29T15:33:39.447Z" }, - { url = "https://files.pythonhosted.org/packages/04/12/7a55b0bdde78a98e2eb2356771fd2dcddb96579e8342bb52aa5bc52e96f0/coverage-7.10.6-cp312-cp312-win32.whl", hash = "sha256:a517feaf3a0a3eca1ee985d8373135cfdedfbba3882a5eab4362bda7c7cf518d", size = 219724, upload-time = "2025-08-29T15:33:41.172Z" }, - { url = "https://files.pythonhosted.org/packages/36/4a/32b185b8b8e327802c9efce3d3108d2fe2d9d31f153a0f7ecfd59c773705/coverage-7.10.6-cp312-cp312-win_amd64.whl", hash = "sha256:856986eadf41f52b214176d894a7de05331117f6035a28ac0016c0f63d887629", size = 220536, upload-time = "2025-08-29T15:33:42.524Z" }, - { url = "https://files.pythonhosted.org/packages/08/3a/d5d8dc703e4998038c3099eaf77adddb00536a3cec08c8dcd556a36a3eb4/coverage-7.10.6-cp312-cp312-win_arm64.whl", hash = "sha256:acf36b8268785aad739443fa2780c16260ee3fa09d12b3a70f772ef100939d80", size = 219171, upload-time = "2025-08-29T15:33:43.974Z" }, - { url = "https://files.pythonhosted.org/packages/bd/e7/917e5953ea29a28c1057729c1d5af9084ab6d9c66217523fd0e10f14d8f6/coverage-7.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ffea0575345e9ee0144dfe5701aa17f3ba546f8c3bb48db62ae101afb740e7d6", size = 217351, upload-time = "2025-08-29T15:33:45.438Z" }, - { url = "https://files.pythonhosted.org/packages/eb/86/2e161b93a4f11d0ea93f9bebb6a53f113d5d6e416d7561ca41bb0a29996b/coverage-7.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:95d91d7317cde40a1c249d6b7382750b7e6d86fad9d8eaf4fa3f8f44cf171e80", size = 217600, upload-time = "2025-08-29T15:33:47.269Z" }, - { url = "https://files.pythonhosted.org/packages/0e/66/d03348fdd8df262b3a7fb4ee5727e6e4936e39e2f3a842e803196946f200/coverage-7.10.6-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e23dd5408fe71a356b41baa82892772a4cefcf758f2ca3383d2aa39e1b7a003", size = 248600, upload-time = "2025-08-29T15:33:48.953Z" }, - { url = "https://files.pythonhosted.org/packages/73/dd/508420fb47d09d904d962f123221bc249f64b5e56aa93d5f5f7603be475f/coverage-7.10.6-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27", size = 251206, upload-time = "2025-08-29T15:33:50.697Z" }, - { url = "https://files.pythonhosted.org/packages/e9/1f/9020135734184f439da85c70ea78194c2730e56c2d18aee6e8ff1719d50d/coverage-7.10.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db4a1d897bbbe7339946ffa2fe60c10cc81c43fab8b062d3fcb84188688174a4", size = 252478, upload-time = "2025-08-29T15:33:52.303Z" }, - { url = "https://files.pythonhosted.org/packages/a4/a4/3d228f3942bb5a2051fde28c136eea23a761177dc4ff4ef54533164ce255/coverage-7.10.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fd7879082953c156d5b13c74aa6cca37f6a6f4747b39538504c3f9c63d043d", size = 250637, upload-time = "2025-08-29T15:33:53.67Z" }, - { url = "https://files.pythonhosted.org/packages/36/e3/293dce8cdb9a83de971637afc59b7190faad60603b40e32635cbd15fbf61/coverage-7.10.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:28395ca3f71cd103b8c116333fa9db867f3a3e1ad6a084aa3725ae002b6583bc", size = 248529, upload-time = "2025-08-29T15:33:55.022Z" }, - { url = "https://files.pythonhosted.org/packages/90/26/64eecfa214e80dd1d101e420cab2901827de0e49631d666543d0e53cf597/coverage-7.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:61c950fc33d29c91b9e18540e1aed7d9f6787cc870a3e4032493bbbe641d12fc", size = 250143, upload-time = "2025-08-29T15:33:56.386Z" }, - { url = "https://files.pythonhosted.org/packages/3e/70/bd80588338f65ea5b0d97e424b820fb4068b9cfb9597fbd91963086e004b/coverage-7.10.6-cp313-cp313-win32.whl", hash = "sha256:160c00a5e6b6bdf4e5984b0ef21fc860bc94416c41b7df4d63f536d17c38902e", size = 219770, upload-time = "2025-08-29T15:33:58.063Z" }, - { url = "https://files.pythonhosted.org/packages/a7/14/0b831122305abcc1060c008f6c97bbdc0a913ab47d65070a01dc50293c2b/coverage-7.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:628055297f3e2aa181464c3808402887643405573eb3d9de060d81531fa79d32", size = 220566, upload-time = "2025-08-29T15:33:59.766Z" }, - { url = "https://files.pythonhosted.org/packages/83/c6/81a83778c1f83f1a4a168ed6673eeedc205afb562d8500175292ca64b94e/coverage-7.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:df4ec1f8540b0bcbe26ca7dd0f541847cc8a108b35596f9f91f59f0c060bfdd2", size = 219195, upload-time = "2025-08-29T15:34:01.191Z" }, - { url = "https://files.pythonhosted.org/packages/d7/1c/ccccf4bf116f9517275fa85047495515add43e41dfe8e0bef6e333c6b344/coverage-7.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c9a8b7a34a4de3ed987f636f71881cd3b8339f61118b1aa311fbda12741bff0b", size = 218059, upload-time = "2025-08-29T15:34:02.91Z" }, - { url = "https://files.pythonhosted.org/packages/92/97/8a3ceff833d27c7492af4f39d5da6761e9ff624831db9e9f25b3886ddbca/coverage-7.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dd5af36092430c2b075cee966719898f2ae87b636cefb85a653f1d0ba5d5393", size = 218287, upload-time = "2025-08-29T15:34:05.106Z" }, - { url = "https://files.pythonhosted.org/packages/92/d8/50b4a32580cf41ff0423777a2791aaf3269ab60c840b62009aec12d3970d/coverage-7.10.6-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b0353b0f0850d49ada66fdd7d0c7cdb0f86b900bb9e367024fd14a60cecc1e27", size = 259625, upload-time = "2025-08-29T15:34:06.575Z" }, - { url = "https://files.pythonhosted.org/packages/7e/7e/6a7df5a6fb440a0179d94a348eb6616ed4745e7df26bf2a02bc4db72c421/coverage-7.10.6-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d6b9ae13d5d3e8aeca9ca94198aa7b3ebbc5acfada557d724f2a1f03d2c0b0df", size = 261801, upload-time = "2025-08-29T15:34:08.006Z" }, - { url = "https://files.pythonhosted.org/packages/3a/4c/a270a414f4ed5d196b9d3d67922968e768cd971d1b251e1b4f75e9362f75/coverage-7.10.6-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:675824a363cc05781b1527b39dc2587b8984965834a748177ee3c37b64ffeafb", size = 264027, upload-time = "2025-08-29T15:34:09.806Z" }, - { url = "https://files.pythonhosted.org/packages/9c/8b/3210d663d594926c12f373c5370bf1e7c5c3a427519a8afa65b561b9a55c/coverage-7.10.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:692d70ea725f471a547c305f0d0fc6a73480c62fb0da726370c088ab21aed282", size = 261576, upload-time = "2025-08-29T15:34:11.585Z" }, - { url = "https://files.pythonhosted.org/packages/72/d0/e1961eff67e9e1dba3fc5eb7a4caf726b35a5b03776892da8d79ec895775/coverage-7.10.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:851430a9a361c7a8484a36126d1d0ff8d529d97385eacc8dfdc9bfc8c2d2cbe4", size = 259341, upload-time = "2025-08-29T15:34:13.159Z" }, - { url = "https://files.pythonhosted.org/packages/3a/06/d6478d152cd189b33eac691cba27a40704990ba95de49771285f34a5861e/coverage-7.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d9369a23186d189b2fc95cc08b8160ba242057e887d766864f7adf3c46b2df21", size = 260468, upload-time = "2025-08-29T15:34:14.571Z" }, - { url = "https://files.pythonhosted.org/packages/ed/73/737440247c914a332f0b47f7598535b29965bf305e19bbc22d4c39615d2b/coverage-7.10.6-cp313-cp313t-win32.whl", hash = "sha256:92be86fcb125e9bda0da7806afd29a3fd33fdf58fba5d60318399adf40bf37d0", size = 220429, upload-time = "2025-08-29T15:34:16.394Z" }, - { url = "https://files.pythonhosted.org/packages/bd/76/b92d3214740f2357ef4a27c75a526eb6c28f79c402e9f20a922c295c05e2/coverage-7.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6b3039e2ca459a70c79523d39347d83b73f2f06af5624905eba7ec34d64d80b5", size = 221493, upload-time = "2025-08-29T15:34:17.835Z" }, - { url = "https://files.pythonhosted.org/packages/fc/8e/6dcb29c599c8a1f654ec6cb68d76644fe635513af16e932d2d4ad1e5ac6e/coverage-7.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3fb99d0786fe17b228eab663d16bee2288e8724d26a199c29325aac4b0319b9b", size = 219757, upload-time = "2025-08-29T15:34:19.248Z" }, - { url = "https://files.pythonhosted.org/packages/d3/aa/76cf0b5ec00619ef208da4689281d48b57f2c7fde883d14bf9441b74d59f/coverage-7.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6008a021907be8c4c02f37cdc3ffb258493bdebfeaf9a839f9e71dfdc47b018e", size = 217331, upload-time = "2025-08-29T15:34:20.846Z" }, - { url = "https://files.pythonhosted.org/packages/65/91/8e41b8c7c505d398d7730206f3cbb4a875a35ca1041efc518051bfce0f6b/coverage-7.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5e75e37f23eb144e78940b40395b42f2321951206a4f50e23cfd6e8a198d3ceb", size = 217607, upload-time = "2025-08-29T15:34:22.433Z" }, - { url = "https://files.pythonhosted.org/packages/87/7f/f718e732a423d442e6616580a951b8d1ec3575ea48bcd0e2228386805e79/coverage-7.10.6-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0f7cb359a448e043c576f0da00aa8bfd796a01b06aa610ca453d4dde09cc1034", size = 248663, upload-time = "2025-08-29T15:34:24.425Z" }, - { url = "https://files.pythonhosted.org/packages/e6/52/c1106120e6d801ac03e12b5285e971e758e925b6f82ee9b86db3aa10045d/coverage-7.10.6-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c68018e4fc4e14b5668f1353b41ccf4bc83ba355f0e1b3836861c6f042d89ac1", size = 251197, upload-time = "2025-08-29T15:34:25.906Z" }, - { url = "https://files.pythonhosted.org/packages/3d/ec/3a8645b1bb40e36acde9c0609f08942852a4af91a937fe2c129a38f2d3f5/coverage-7.10.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd4b2b0707fc55afa160cd5fc33b27ccbf75ca11d81f4ec9863d5793fc6df56a", size = 252551, upload-time = "2025-08-29T15:34:27.337Z" }, - { url = "https://files.pythonhosted.org/packages/a1/70/09ecb68eeb1155b28a1d16525fd3a9b65fbe75337311a99830df935d62b6/coverage-7.10.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cec13817a651f8804a86e4f79d815b3b28472c910e099e4d5a0e8a3b6a1d4cb", size = 250553, upload-time = "2025-08-29T15:34:29.065Z" }, - { url = "https://files.pythonhosted.org/packages/c6/80/47df374b893fa812e953b5bc93dcb1427a7b3d7a1a7d2db33043d17f74b9/coverage-7.10.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f2a6a8e06bbda06f78739f40bfb56c45d14eb8249d0f0ea6d4b3d48e1f7c695d", size = 248486, upload-time = "2025-08-29T15:34:30.897Z" }, - { url = "https://files.pythonhosted.org/packages/4a/65/9f98640979ecee1b0d1a7164b589de720ddf8100d1747d9bbdb84be0c0fb/coverage-7.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:081b98395ced0d9bcf60ada7661a0b75f36b78b9d7e39ea0790bb4ed8da14747", size = 249981, upload-time = "2025-08-29T15:34:32.365Z" }, - { url = "https://files.pythonhosted.org/packages/1f/55/eeb6603371e6629037f47bd25bef300387257ed53a3c5fdb159b7ac8c651/coverage-7.10.6-cp314-cp314-win32.whl", hash = "sha256:6937347c5d7d069ee776b2bf4e1212f912a9f1f141a429c475e6089462fcecc5", size = 220054, upload-time = "2025-08-29T15:34:34.124Z" }, - { url = "https://files.pythonhosted.org/packages/15/d1/a0912b7611bc35412e919a2cd59ae98e7ea3b475e562668040a43fb27897/coverage-7.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:adec1d980fa07e60b6ef865f9e5410ba760e4e1d26f60f7e5772c73b9a5b0713", size = 220851, upload-time = "2025-08-29T15:34:35.651Z" }, - { url = "https://files.pythonhosted.org/packages/ef/2d/11880bb8ef80a45338e0b3e0725e4c2d73ffbb4822c29d987078224fd6a5/coverage-7.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:a80f7aef9535442bdcf562e5a0d5a5538ce8abe6bb209cfbf170c462ac2c2a32", size = 219429, upload-time = "2025-08-29T15:34:37.16Z" }, - { url = "https://files.pythonhosted.org/packages/83/c0/1f00caad775c03a700146f55536ecd097a881ff08d310a58b353a1421be0/coverage-7.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0de434f4fbbe5af4fa7989521c655c8c779afb61c53ab561b64dcee6149e4c65", size = 218080, upload-time = "2025-08-29T15:34:38.919Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c4/b1c5d2bd7cc412cbeb035e257fd06ed4e3e139ac871d16a07434e145d18d/coverage-7.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6e31b8155150c57e5ac43ccd289d079eb3f825187d7c66e755a055d2c85794c6", size = 218293, upload-time = "2025-08-29T15:34:40.425Z" }, - { url = "https://files.pythonhosted.org/packages/3f/07/4468d37c94724bf6ec354e4ec2f205fda194343e3e85fd2e59cec57e6a54/coverage-7.10.6-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:98cede73eb83c31e2118ae8d379c12e3e42736903a8afcca92a7218e1f2903b0", size = 259800, upload-time = "2025-08-29T15:34:41.996Z" }, - { url = "https://files.pythonhosted.org/packages/82/d8/f8fb351be5fee31690cd8da768fd62f1cfab33c31d9f7baba6cd8960f6b8/coverage-7.10.6-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f863c08f4ff6b64fa8045b1e3da480f5374779ef187f07b82e0538c68cb4ff8e", size = 261965, upload-time = "2025-08-29T15:34:43.61Z" }, - { url = "https://files.pythonhosted.org/packages/e8/70/65d4d7cfc75c5c6eb2fed3ee5cdf420fd8ae09c4808723a89a81d5b1b9c3/coverage-7.10.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b38261034fda87be356f2c3f42221fdb4171c3ce7658066ae449241485390d5", size = 264220, upload-time = "2025-08-29T15:34:45.387Z" }, - { url = "https://files.pythonhosted.org/packages/98/3c/069df106d19024324cde10e4ec379fe2fb978017d25e97ebee23002fbadf/coverage-7.10.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e93b1476b79eae849dc3872faeb0bf7948fd9ea34869590bc16a2a00b9c82a7", size = 261660, upload-time = "2025-08-29T15:34:47.288Z" }, - { url = "https://files.pythonhosted.org/packages/fc/8a/2974d53904080c5dc91af798b3a54a4ccb99a45595cc0dcec6eb9616a57d/coverage-7.10.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ff8a991f70f4c0cf53088abf1e3886edcc87d53004c7bb94e78650b4d3dac3b5", size = 259417, upload-time = "2025-08-29T15:34:48.779Z" }, - { url = "https://files.pythonhosted.org/packages/30/38/9616a6b49c686394b318974d7f6e08f38b8af2270ce7488e879888d1e5db/coverage-7.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ac765b026c9f33044419cbba1da913cfb82cca1b60598ac1c7a5ed6aac4621a0", size = 260567, upload-time = "2025-08-29T15:34:50.718Z" }, - { url = "https://files.pythonhosted.org/packages/76/16/3ed2d6312b371a8cf804abf4e14895b70e4c3491c6e53536d63fd0958a8d/coverage-7.10.6-cp314-cp314t-win32.whl", hash = "sha256:441c357d55f4936875636ef2cfb3bee36e466dcf50df9afbd398ce79dba1ebb7", size = 220831, upload-time = "2025-08-29T15:34:52.653Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e5/d38d0cb830abede2adb8b147770d2a3d0e7fecc7228245b9b1ae6c24930a/coverage-7.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:073711de3181b2e204e4870ac83a7c4853115b42e9cd4d145f2231e12d670930", size = 221950, upload-time = "2025-08-29T15:34:54.212Z" }, - { url = "https://files.pythonhosted.org/packages/f4/51/e48e550f6279349895b0ffcd6d2a690e3131ba3a7f4eafccc141966d4dea/coverage-7.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:137921f2bac5559334ba66122b753db6dc5d1cf01eb7b64eb412bb0d064ef35b", size = 219969, upload-time = "2025-08-29T15:34:55.83Z" }, - { url = "https://files.pythonhosted.org/packages/91/70/f73ad83b1d2fd2d5825ac58c8f551193433a7deaf9b0d00a8b69ef61cd9a/coverage-7.10.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90558c35af64971d65fbd935c32010f9a2f52776103a259f1dee865fe8259352", size = 217009, upload-time = "2025-08-29T15:34:57.381Z" }, - { url = "https://files.pythonhosted.org/packages/01/e8/099b55cd48922abbd4b01ddd9ffa352408614413ebfc965501e981aced6b/coverage-7.10.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8953746d371e5695405806c46d705a3cd170b9cc2b9f93953ad838f6c1e58612", size = 217400, upload-time = "2025-08-29T15:34:58.985Z" }, - { url = "https://files.pythonhosted.org/packages/ee/d1/c6bac7c9e1003110a318636fef3b5c039df57ab44abcc41d43262a163c28/coverage-7.10.6-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c83f6afb480eae0313114297d29d7c295670a41c11b274e6bca0c64540c1ce7b", size = 243835, upload-time = "2025-08-29T15:35:00.541Z" }, - { url = "https://files.pythonhosted.org/packages/01/f9/82c6c061838afbd2172e773156c0aa84a901d59211b4975a4e93accf5c89/coverage-7.10.6-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7eb68d356ba0cc158ca535ce1381dbf2037fa8cb5b1ae5ddfc302e7317d04144", size = 245658, upload-time = "2025-08-29T15:35:02.135Z" }, - { url = "https://files.pythonhosted.org/packages/81/6a/35674445b1d38161148558a3ff51b0aa7f0b54b1def3abe3fbd34efe05bc/coverage-7.10.6-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b15a87265e96307482746d86995f4bff282f14b027db75469c446da6127433b", size = 247433, upload-time = "2025-08-29T15:35:03.777Z" }, - { url = "https://files.pythonhosted.org/packages/18/27/98c99e7cafb288730a93535092eb433b5503d529869791681c4f2e2012a8/coverage-7.10.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fc53ba868875bfbb66ee447d64d6413c2db91fddcfca57025a0e7ab5b07d5862", size = 245315, upload-time = "2025-08-29T15:35:05.629Z" }, - { url = "https://files.pythonhosted.org/packages/09/05/123e0dba812408c719c319dea05782433246f7aa7b67e60402d90e847545/coverage-7.10.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:efeda443000aa23f276f4df973cb82beca682fd800bb119d19e80504ffe53ec2", size = 243385, upload-time = "2025-08-29T15:35:07.494Z" }, - { url = "https://files.pythonhosted.org/packages/67/52/d57a42502aef05c6325f28e2e81216c2d9b489040132c18725b7a04d1448/coverage-7.10.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9702b59d582ff1e184945d8b501ffdd08d2cee38d93a2206aa5f1365ce0b8d78", size = 244343, upload-time = "2025-08-29T15:35:09.55Z" }, - { url = "https://files.pythonhosted.org/packages/6b/22/7f6fad7dbb37cf99b542c5e157d463bd96b797078b1ec506691bc836f476/coverage-7.10.6-cp39-cp39-win32.whl", hash = "sha256:2195f8e16ba1a44651ca684db2ea2b2d4b5345da12f07d9c22a395202a05b23c", size = 219530, upload-time = "2025-08-29T15:35:11.167Z" }, - { url = "https://files.pythonhosted.org/packages/62/30/e2fda29bfe335026027e11e6a5e57a764c9df13127b5cf42af4c3e99b937/coverage-7.10.6-cp39-cp39-win_amd64.whl", hash = "sha256:f32ff80e7ef6a5b5b606ea69a36e97b219cd9dc799bcf2963018a4d8f788cfbf", size = 220432, upload-time = "2025-08-29T15:35:12.902Z" }, - { url = "https://files.pythonhosted.org/packages/44/0c/50db5379b615854b5cf89146f8f5bd1d5a9693d7f3a987e269693521c404/coverage-7.10.6-py3-none-any.whl", hash = "sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3", size = 208986, upload-time = "2025-08-29T15:35:14.506Z" }, -] - -[package.optional-dependencies] -toml = [ - { name = "tomli", marker = "python_full_version >= '3.9' and python_full_version <= '3.11'" }, -] - -[[package]] -name = "exceptiongroup" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, - { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, -] - -[[package]] -name = "execnet" -version = "2.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bb/ff/b4c0dc78fbe20c3e59c0c7334de0c27eb4001a2b2017999af398bf730817/execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3", size = 166524, upload-time = "2024-04-08T09:04:19.245Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/09/2aea36ff60d16dd8879bdb2f5b3ee0ba8d08cbbdcdfe870e695ce3784385/execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc", size = 40612, upload-time = "2024-04-08T09:04:17.414Z" }, -] [[package]] name = "flashvm" +version = "0.1.0" source = { editable = "." } - -[package.optional-dependencies] -dev = [ - { name = "hypothesis", version = "6.113.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, - { name = "hypothesis", version = "6.138.14", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, - { name = "maturin" }, - { name = "pytest", version = "8.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, - { name = "pytest", version = "8.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, - { name = "pytest-asyncio", version = "0.24.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, - { name = "pytest-asyncio", version = "1.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, - { name = "pytest-benchmark", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, - { name = "pytest-benchmark", version = "5.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, - { name = "pytest-cov", version = "5.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, - { name = "pytest-cov", version = "6.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, - { name = "pytest-mock" }, - { name = "pytest-timeout" }, - { name = "pytest-xdist", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, - { name = "pytest-xdist", version = "3.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, -] -test = [ - { name = "pytest", version = "8.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, - { name = "pytest", version = "8.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, - { name = "pytest-cov", version = "5.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, - { name = "pytest-cov", version = "6.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, - { name = "pytest-timeout" }, -] - -[package.dev-dependencies] -dev = [ - { name = "pytest-xdist", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, - { name = "pytest-xdist", version = "3.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, -] - -[package.metadata] -requires-dist = [ - { name = "hypothesis", marker = "extra == 'dev'", specifier = ">=6.75" }, - { name = "maturin", marker = "extra == 'dev'", specifier = ">=1.0,<2.0" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0" }, - { name = "pytest", marker = "extra == 'test'", specifier = ">=7.0" }, - { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.21" }, - { name = "pytest-benchmark", marker = "extra == 'dev'", specifier = ">=4.0" }, - { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.0" }, - { name = "pytest-cov", marker = "extra == 'test'", specifier = ">=4.0" }, - { name = "pytest-mock", marker = "extra == 'dev'", specifier = ">=3.10" }, - { name = "pytest-timeout", marker = "extra == 'dev'", specifier = ">=2.1" }, - { name = "pytest-timeout", marker = "extra == 'test'", specifier = ">=2.1" }, - { name = "pytest-xdist", marker = "extra == 'dev'", specifier = ">=3.0" }, -] -provides-extras = ["dev", "test"] - -[package.metadata.requires-dev] -dev = [{ name = "pytest-xdist", specifier = ">=3.6.1" }] - -[[package]] -name = "hypothesis" -version = "6.113.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.9'", -] -dependencies = [ - { name = "attrs", marker = "python_full_version < '3.9'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.9'" }, - { name = "sortedcontainers", marker = "python_full_version < '3.9'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/32/6513cd7256f38c19a6c8a1d5ce9792bcd35c7f11651989994731f0e97672/hypothesis-6.113.0.tar.gz", hash = "sha256:5556ac66fdf72a4ccd5d237810f7cf6bdcd00534a4485015ef881af26e20f7c7", size = 408897, upload-time = "2024-10-09T03:51:05.707Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/fa/4acb477b86a94571958bd337eae5baf334d21b8c98a04b594d0dad381ba8/hypothesis-6.113.0-py3-none-any.whl", hash = "sha256:d539180eb2bb71ed28a23dfe94e67c851f9b09f3ccc4125afad43f17e32e2bad", size = 469790, upload-time = "2024-10-09T03:51:02.629Z" }, -] - -[[package]] -name = "hypothesis" -version = "6.138.14" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.9'", -] -dependencies = [ - { name = "attrs", marker = "python_full_version >= '3.9'" }, - { name = "exceptiongroup", marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, - { name = "sortedcontainers", marker = "python_full_version >= '3.9'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/67/ed/2f65a358dd6b5bf3cee99dd2d3473eb9c5d5e50b50f83bbf8ef89ea96e39/hypothesis-6.138.14.tar.gz", hash = "sha256:5c1aa1ce3f1094b5c04ea03476017695bda408a174330e5275e40ddd06d3307a", size = 466152, upload-time = "2025-09-02T21:37:20.883Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/55/2e/fcf7371887f45083472165b21b68a7f552049334e01dc3febe484bbb7bc4/hypothesis-6.138.14-py3-none-any.whl", hash = "sha256:1a702ecfff7034b3252d7a83328093388641cdba863197169559839e841c2154", size = 533626, upload-time = "2025-09-02T21:37:17.318Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, -] - -[[package]] -name = "maturin" -version = "1.9.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "tomli", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/13/7c/b11b870fc4fd84de2099906314ce45488ae17be32ff5493519a6cddc518a/maturin-1.9.4.tar.gz", hash = "sha256:235163a0c99bc6f380fb8786c04fd14dcf6cd622ff295ea3de525015e6ac40cf", size = 213647, upload-time = "2025-08-27T11:37:57.079Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/90/0d99389eea1939116fca841cad0763600c8d3183a02a9478d066736c60e8/maturin-1.9.4-py3-none-linux_armv6l.whl", hash = "sha256:6ff37578e3f5fdbe685110d45f60af1f5a7dfce70a1e26dfe3810af66853ecae", size = 8276133, upload-time = "2025-08-27T11:37:23.325Z" }, - { url = "https://files.pythonhosted.org/packages/f4/ed/c8ec68b383e50f084bf1fa9605e62a90cd32a3f75d9894ed3a6e5d4cc5b3/maturin-1.9.4-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:f3837bb53611b2dafa1c090436c330f2d743ba305ef00d8801a371f4495e7e1b", size = 15994496, upload-time = "2025-08-27T11:37:27.092Z" }, - { url = "https://files.pythonhosted.org/packages/84/4e/401ff5f3cfc6b123364d4b94379bf910d7baee32c9c95b72784ff2329357/maturin-1.9.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4227d627d8e3bfe45877a8d65e9d8351a9d01434549f0da75d2c06a1b570de58", size = 8362228, upload-time = "2025-08-27T11:37:31.181Z" }, - { url = "https://files.pythonhosted.org/packages/51/8e/c56176dd360da9650c62b8a5ecfb85432cf011e97e46c186901e6996002e/maturin-1.9.4-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:1bb2aa0fa29032e9c5aac03ac400396ddea12cadef242f8967e9c8ef715313a1", size = 8271397, upload-time = "2025-08-27T11:37:33.672Z" }, - { url = "https://files.pythonhosted.org/packages/d2/46/001fcc5c6ad509874896418d6169a61acd619df5b724f99766308c44a99f/maturin-1.9.4-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:a0868d52934c8a5d1411b42367633fdb5cd5515bec47a534192282167448ec30", size = 8775625, upload-time = "2025-08-27T11:37:35.86Z" }, - { url = "https://files.pythonhosted.org/packages/b4/2e/26fa7574f01c19b7a74680fd70e5bae2e8c40fed9683d1752e765062cc2b/maturin-1.9.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:68b7b833b25741c0f553b78e8b9e095b31ae7c6611533b3c7b71f84c2cb8fc44", size = 8051117, upload-time = "2025-08-27T11:37:38.278Z" }, - { url = "https://files.pythonhosted.org/packages/73/ee/ca7308832d4f5b521c1aa176d9265f6f93e0bd1ad82a90fd9cd799f6b28c/maturin-1.9.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:08dc86312afee55af778af919818632e35d8d0464ccd79cb86700d9ea560ccd7", size = 8132122, upload-time = "2025-08-27T11:37:40.499Z" }, - { url = "https://files.pythonhosted.org/packages/45/e8/c623955da75e801a06942edf1fdc4e772a9e8fbc1ceebbdc85d59584dc10/maturin-1.9.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:ef20ffdd943078c4c3699c29fb2ed722bb6b4419efdade6642d1dbf248f94a70", size = 10586762, upload-time = "2025-08-27T11:37:42.718Z" }, - { url = "https://files.pythonhosted.org/packages/3c/4b/19ad558fdf54e151b1b4916ed45f1952ada96684ee6db64f9cd91cabec09/maturin-1.9.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:368e958468431dfeec80f75eea9639b4356d8c42428b0128444424b083fecfb0", size = 8926988, upload-time = "2025-08-27T11:37:45.492Z" }, - { url = "https://files.pythonhosted.org/packages/7e/27/153ad15eccae26921e8a01812da9f3b7f9013368f8f92c36853f2043b2a3/maturin-1.9.4-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:273f879214f63f79bfe851cd7d541f8150bdbfae5dfdc3c0c4d125d02d1f41b4", size = 8536758, upload-time = "2025-08-27T11:37:48.213Z" }, - { url = "https://files.pythonhosted.org/packages/43/e3/f304c3bdc3fba9adebe5348d4d2dd015f1152c0a9027aaf52cae0bb182c8/maturin-1.9.4-py3-none-win32.whl", hash = "sha256:ed2e54d132ace7e61829bd49709331007dd9a2cc78937f598aa76a4f69b6804d", size = 7265200, upload-time = "2025-08-27T11:37:50.881Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/f86d0124bf1816b99005c058a1dbdca7cb5850d9cf4b09dcae07a1bc6201/maturin-1.9.4-py3-none-win_amd64.whl", hash = "sha256:8e450bb2c9afdf38a0059ee2e1ec2b17323f152b59c16f33eb9c74edaf1f9f79", size = 8237391, upload-time = "2025-08-27T11:37:53.23Z" }, - { url = "https://files.pythonhosted.org/packages/3f/25/8320fc2591e45b750c3ae71fa596b47aefa802d07d6abaaa719034a85160/maturin-1.9.4-py3-none-win_arm64.whl", hash = "sha256:7a6f980a9b67a5c13c844c268eabd855b54a6a765df4b4bb07d15a990572a4c9", size = 6988277, upload-time = "2025-08-27T11:37:55.429Z" }, -] - -[[package]] -name = "packaging" -version = "25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, -] - -[[package]] -name = "pluggy" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.9'", -] -sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955, upload-time = "2024-04-20T21:34:42.531Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload-time = "2024-04-20T21:34:40.434Z" }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.9'", -] -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, -] - -[[package]] -name = "py-cpuinfo" -version = "9.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/37/a8/d832f7293ebb21690860d2e01d8115e5ff6f2ae8bbdc953f0eb0fa4bd2c7/py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690", size = 104716, upload-time = "2022-10-25T20:38:06.303Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5", size = 22335, upload-time = "2022-10-25T20:38:27.636Z" }, -] - -[[package]] -name = "pygments" -version = "2.19.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, -] - -[[package]] -name = "pytest" -version = "8.3.5" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.9'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version < '3.9' and sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.9'" }, - { name = "iniconfig", marker = "python_full_version < '3.9'" }, - { name = "packaging", marker = "python_full_version < '3.9'" }, - { name = "pluggy", version = "1.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, - { name = "tomli", marker = "python_full_version < '3.9'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891, upload-time = "2025-03-02T12:54:54.503Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634, upload-time = "2025-03-02T12:54:52.069Z" }, -] - -[[package]] -name = "pytest" -version = "8.4.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.9'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version >= '3.9' and sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, - { name = "iniconfig", marker = "python_full_version >= '3.9'" }, - { name = "packaging", marker = "python_full_version >= '3.9'" }, - { name = "pluggy", version = "1.6.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, - { name = "pygments", marker = "python_full_version >= '3.9'" }, - { name = "tomli", marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, -] - -[[package]] -name = "pytest-asyncio" -version = "0.24.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.9'", -] -dependencies = [ - { name = "pytest", version = "8.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/52/6d/c6cf50ce320cf8611df7a1254d86233b3df7cc07f9b5f5cbcb82e08aa534/pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276", size = 49855, upload-time = "2024-08-22T08:03:18.145Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/31/6607dab48616902f76885dfcf62c08d929796fc3b2d2318faf9fd54dbed9/pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b", size = 18024, upload-time = "2024-08-22T08:03:15.536Z" }, -] - -[[package]] -name = "pytest-asyncio" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.9'", -] -dependencies = [ - { name = "backports-asyncio-runner", marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, - { name = "pytest", version = "8.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, - { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4e/51/f8794af39eeb870e87a8c8068642fc07bce0c854d6865d7dd0f2a9d338c2/pytest_asyncio-1.1.0.tar.gz", hash = "sha256:796aa822981e01b68c12e4827b8697108f7205020f24b5793b3c41555dab68ea", size = 46652, upload-time = "2025-07-16T04:29:26.393Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157, upload-time = "2025-07-16T04:29:24.929Z" }, -] - -[[package]] -name = "pytest-benchmark" -version = "4.0.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.9'", -] -dependencies = [ - { name = "py-cpuinfo", marker = "python_full_version < '3.9'" }, - { name = "pytest", version = "8.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/08/e6b0067efa9a1f2a1eb3043ecd8a0c48bfeb60d3255006dcc829d72d5da2/pytest-benchmark-4.0.0.tar.gz", hash = "sha256:fb0785b83efe599a6a956361c0691ae1dbb5318018561af10f3e915caa0048d1", size = 334641, upload-time = "2022-10-25T21:21:55.686Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/a1/3b70862b5b3f830f0422844f25a823d0470739d994466be9dbbbb414d85a/pytest_benchmark-4.0.0-py3-none-any.whl", hash = "sha256:fdb7db64e31c8b277dff9850d2a2556d8b60bcb0ea6524e36e28ffd7c87f71d6", size = 43951, upload-time = "2022-10-25T21:21:53.208Z" }, -] - -[[package]] -name = "pytest-benchmark" -version = "5.1.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.9'", -] -dependencies = [ - { name = "py-cpuinfo", marker = "python_full_version >= '3.9'" }, - { name = "pytest", version = "8.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/39/d0/a8bd08d641b393db3be3819b03e2d9bb8760ca8479080a26a5f6e540e99c/pytest-benchmark-5.1.0.tar.gz", hash = "sha256:9ea661cdc292e8231f7cd4c10b0319e56a2118e2c09d9f50e1b3d150d2aca105", size = 337810, upload-time = "2024-10-30T11:51:48.521Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/d6/b41653199ea09d5969d4e385df9bbfd9a100f28ca7e824ce7c0a016e3053/pytest_benchmark-5.1.0-py3-none-any.whl", hash = "sha256:922de2dfa3033c227c96da942d1878191afa135a29485fb942e85dff1c592c89", size = 44259, upload-time = "2024-10-30T11:51:45.94Z" }, -] - -[[package]] -name = "pytest-cov" -version = "5.0.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.9'", -] -dependencies = [ - { name = "coverage", version = "7.6.1", source = { registry = "https://pypi.org/simple" }, extra = ["toml"], marker = "python_full_version < '3.9'" }, - { name = "pytest", version = "8.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/74/67/00efc8d11b630c56f15f4ad9c7f9223f1e5ec275aaae3fa9118c6a223ad2/pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857", size = 63042, upload-time = "2024-03-24T20:16:34.856Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/3a/af5b4fa5961d9a1e6237b530eb87dd04aea6eb83da09d2a4073d81b54ccf/pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652", size = 21990, upload-time = "2024-03-24T20:16:32.444Z" }, -] - -[[package]] -name = "pytest-cov" -version = "6.2.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.9'", -] -dependencies = [ - { name = "coverage", version = "7.10.6", source = { registry = "https://pypi.org/simple" }, extra = ["toml"], marker = "python_full_version >= '3.9'" }, - { name = "pluggy", version = "1.6.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, - { name = "pytest", version = "8.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432, upload-time = "2025-06-12T10:47:47.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" }, -] - -[[package]] -name = "pytest-mock" -version = "3.14.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest", version = "8.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, - { name = "pytest", version = "8.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/28/67172c96ba684058a4d24ffe144d64783d2a270d0af0d9e792737bddc75c/pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e", size = 33241, upload-time = "2025-05-26T13:58:45.167Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/05/77b60e520511c53d1c1ca75f1930c7dd8e971d0c4379b7f4b3f9644685ba/pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0", size = 9923, upload-time = "2025-05-26T13:58:43.487Z" }, -] - -[[package]] -name = "pytest-timeout" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest", version = "8.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, - { name = "pytest", version = "8.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ac/82/4c9ecabab13363e72d880f2fb504c5f750433b2b6f16e99f4ec21ada284c/pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a", size = 17973, upload-time = "2025-05-05T19:44:34.99Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", size = 14382, upload-time = "2025-05-05T19:44:33.502Z" }, -] - -[[package]] -name = "pytest-xdist" -version = "3.6.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.9'", -] -dependencies = [ - { name = "execnet", marker = "python_full_version < '3.9'" }, - { name = "pytest", version = "8.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/41/c4/3c310a19bc1f1e9ef50075582652673ef2bfc8cd62afef9585683821902f/pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d", size = 84060, upload-time = "2024-04-28T19:29:54.414Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/82/1d96bf03ee4c0fdc3c0cbe61470070e659ca78dc0086fb88b66c185e2449/pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7", size = 46108, upload-time = "2024-04-28T19:29:52.813Z" }, -] - -[[package]] -name = "pytest-xdist" -version = "3.8.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.9'", -] -dependencies = [ - { name = "execnet", marker = "python_full_version >= '3.9'" }, - { name = "pytest", version = "8.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069, upload-time = "2025-07-01T13:30:59.346Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", size = 46396, upload-time = "2025-07-01T13:30:56.632Z" }, -] - -[[package]] -name = "sortedcontainers" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, -] - -[[package]] -name = "tomli" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, - { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, - { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, - { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, - { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, - { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, - { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, - { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, - { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, - { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, - { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, - { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, - { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, - { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, - { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, - { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, - { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, - { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, - { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, - { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, - { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, - { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, - { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, - { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, - { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, - { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.13.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.9'", -] -sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.9'", -] -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, -] From 38d023ebdbcaa30b048eb67d2ded3e7d8ba8b9c7 Mon Sep 17 00:00:00 2001 From: fullzer4 Date: Sun, 14 Sep 2025 18:45:39 -0300 Subject: [PATCH 2/2] docs(features): documentation features to implement --- default.nix | 2 ++ flake.lock | 8 ++++---- flake.nix | 45 +++++++++++++++++++++++------------------ package.nix | 23 +++++++++++++++++++++ src/lib.rs | 26 ++++++++++++++++++++---- src/types.rs | 47 +++++++++++++++++++++++++++++++------------ src/vmm/boot.rs | 14 +++++++++---- src/vmm/devices.rs | 14 +++++++++++-- src/vmm/event_loop.rs | 11 +++++++++- src/vmm/kvm_ctx.rs | 11 +++++++++- src/vmm/memory.rs | 5 +++-- src/vmm/run.rs | 8 +++++++- 12 files changed, 162 insertions(+), 52 deletions(-) create mode 100644 default.nix create mode 100644 package.nix diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..e63577a --- /dev/null +++ b/default.nix @@ -0,0 +1,2 @@ +(let flake = builtins.getFlake (toString ./.); in + flake.packages.${builtins.currentSystem}.default) \ No newline at end of file diff --git a/flake.lock b/flake.lock index 40e2620..9769642 100644 --- a/flake.lock +++ b/flake.lock @@ -2,16 +2,16 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1757745802, - "narHash": "sha256-hLEO2TPj55KcUFUU1vgtHE9UEIOjRcH/4QbmfHNF820=", + "lastModified": 1735563628, + "narHash": "sha256-OnSAY7XDSx7CtDoqNh8jwVwh4xNL/2HaJxGjryLWzX8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c23193b943c6c689d70ee98ce3128239ed9e32d1", + "rev": "b134951a4c9f3c995fd7be05f3243f8ecd65d798", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-unstable", + "ref": "nixos-24.05", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index 0f2c53c..bb14baf 100644 --- a/flake.nix +++ b/flake.nix @@ -1,28 +1,33 @@ { - description = "flashVM"; + description = "flashvm flake (packages + devShell)"; - inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05"; outputs = { self, nixpkgs }: - let - systems = [ "x86_64-linux" "aarch64-linux" ]; - forAll = nixpkgs.lib.genAttrs systems; - in { - devShells = forAll (system: - let pkgs = import nixpkgs { inherit system; }; - in { + let + systems = [ "x86_64-linux" "aarch64-linux" ]; + forAllSystems = f: + nixpkgs.lib.genAttrs systems (system: + let pkgs = import nixpkgs { inherit system; }; in f pkgs system); + in { + packages = forAllSystems (pkgs: system: { + default = pkgs.callPackage ./package.nix { inherit pkgs; }; + }); + + devShells = forAllSystems (pkgs: system: { default = pkgs.mkShell { - packages = [ - pkgs.python312 pkgs.maturin pkgs.uv - pkgs.rustc pkgs.cargo pkgs.pkg-config - pkgs.docker pkgs.docker-buildx pkgs.gnutar pkgs.coreutils pkgs.jq - pkgs.umoci pkgs.skopeo - pkgs.erofs-utils pkgs.squashfsTools pkgs.zstd pkgs.lz4 - pkgs.busybox pkgs.cpio pkgs.e2fsprogs pkgs.cloud-hypervisor + buildInputs = [ + pkgs.rustc + pkgs.cargo + pkgs.maturin + pkgs.python311 + pkgs.pkg-config + pkgs.openssl ]; - shellHook = ''echo ">> flashVM dev shell ativa"''; + shellHook = '' + export PYO3_PYTHON=${pkgs.python311}/bin/python3 + ''; }; - } - ); - }; + }); + }; } diff --git a/package.nix b/package.nix new file mode 100644 index 0000000..8c45541 --- /dev/null +++ b/package.nix @@ -0,0 +1,23 @@ +{ pkgs +, python ? pkgs.python311 +}: + +let + py = python.pkgs; +in +py.buildPythonPackage { + pname = "flashvm"; + version = "0.1.0"; + + format = "pyproject"; + src = ./.; + + nativeBuildInputs = [ + pkgs.maturin + pkgs.rustc + pkgs.cargo + pkgs.pkg-config + ] ++ pkgs.lib.optionals pkgs.stdenv.isLinux [ pkgs.openssl ]; + + doCheck = false; +} diff --git a/src/lib.rs b/src/lib.rs index 6d5376a..f67542f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,8 +11,26 @@ use crate::types::{ImageHandle, RunOptions, RunResult}; #[pyfunction] -fn prepare_image<'py>(_py: Python<'py>, meta: Bound<'py, PyDict>) -> PyResult { - // TODO: validar dict (kernel, rootfs_img, initrd opcional, cache_key) +fn prepare_image<'py>(py: Python<'py>, meta: Bound<'py, PyDict>) -> PyResult { + // Aviso se base="embedded" + packages (DEV only) + // TODO(meta): o resolvedor de assets "embedded" deve viver no Python, e retornar paths prontos + if let (Some(base_obj), Some(pkgs_obj)) = (meta.get_item("base")?, meta.get_item("packages")?) { + let base: String = base_obj.extract()?; + let pkgs: Vec = pkgs_obj.extract().unwrap_or_default(); + if base == "embedded" && !pkgs.is_empty() { + let warnings = pyo3::types::PyModule::import_bound(py, "warnings")?; + let warn_type = py.get_type_bound::(); + warnings.call_method1( + "warn", + ( + "DEV-only base. For production, prefer `flashvm build -p ...` to create a pinned OCI image.", + warn_type, + ), + )?; + } + } + + // Validar/transformar meta em ImageHandle ImageHandle::from_pydict(&meta) } @@ -30,8 +48,8 @@ fn run<'py>(py: Python<'py>, image: &ImageHandle, code: &str, opts: Option(_py: Python<'py>, m: Bound<'py, PyModule>) -> PyResult<()> { - m.add_function(wrap_pyfunction_bound!(prepare_image, &m)?)?; - m.add_function(wrap_pyfunction_bound!(run, &m)?)?; + m.add_function(wrap_pyfunction!(prepare_image, &m)?)?; + m.add_function(wrap_pyfunction!(run, &m)?)?; m.add_class::()?; m.add_class::()?; m.add_class::()?; diff --git a/src/types.rs b/src/types.rs index e1b557a..3c087cd 100644 --- a/src/types.rs +++ b/src/types.rs @@ -10,24 +10,45 @@ pub struct ImageHandle { #[pyo3(get)] pub rootfs_img: String, #[pyo3(get)] pub initrd: Option, #[pyo3(get)] pub cache_key: Option, + // metadados opcionais + #[pyo3(get)] pub base: Option, + #[pyo3(get)] pub packages: Option>, } impl ImageHandle { pub fn from_pydict<'py>(d: &pyo3::Bound<'py, PyDict>) -> PyResult { - // TODO: validação robusta + normalização de paths - Ok(Self { - kernel: d - .get_item("kernel")? - .ok_or_else(|| pyo3::exceptions::PyValueError::new_err("missing kernel"))? - .extract()?, - rootfs_img: d - .get_item("rootfs_img")? - .ok_or_else(|| pyo3::exceptions::PyValueError::new_err("missing rootfs_img"))? - .extract()?, - initrd: d.get_item("initrd")?.map(|v| v.extract()).transpose()?, - cache_key: d.get_item("cache_key")?.map(|v| v.extract()).transpose()?, - }) + // metadados opcionais + // TODO(meta): considerar validação mais estrita (tipos, normalização de paths) + let base: Option = d.get_item("base")?.map(|v| v.extract()).transpose()?; + let packages: Option> = d.get_item("packages")?.map(|v| v.extract()).transpose()?; + + // modo explícito: kernel + rootfs_img presentes + let kernel = d.get_item("kernel")?.map(|v| v.extract()).transpose()?; + let rootfs_img = d.get_item("rootfs_img")?.map(|v| v.extract()).transpose()?; + + match (kernel, rootfs_img) { + (Some(kernel), Some(rootfs_img)) => Ok(Self { + kernel, + rootfs_img, + initrd: d.get_item("initrd")?.map(|v| v.extract()).transpose()?, + cache_key: d.get_item("cache_key")?.map(|v| v.extract()).transpose()?, + base, + packages, + }), + _ => { + // modo embedded sem caminhos explícitos: erro orientando o chamador + if matches!(base.as_deref(), Some("embedded")) { + Err(pyo3::exceptions::PyValueError::new_err( + "base='embedded' requer que o chamador Python resolva paths (kernel/rootfs_img) dos assets do pacote e reenvie no meta", + )) + } else { + Err(pyo3::exceptions::PyValueError::new_err( + "missing kernel/rootfs_img", + )) + } + } + } } } diff --git a/src/vmm/boot.rs b/src/vmm/boot.rs index ca8abf7..42127c8 100644 --- a/src/vmm/boot.rs +++ b/src/vmm/boot.rs @@ -1,7 +1,7 @@ use anyhow::Result; use linux_loader::loader::{Elf, KernelLoader}; use linux_loader::cmdline::Cmdline; -use vm_memory::{GuestMemoryMmap, GuestAddress}; +use vm_memory::{GuestMemoryMmap, GuestAddress, Bytes}; use std::path::Path; @@ -9,7 +9,10 @@ pub struct BootInfo { pub entry: GuestAddress, pub cmdline_addr: GuestAddress } pub fn load_kernel(gm: &GuestMemoryMmap, kernel_path: &Path, cmdline: &str) -> Result { - // TODO: suportar bzImage; por ora, ELF vmlinux + // TODO(boot): trocar para loader de bzImage para x86_64 (mais simples) e preparar boot params + // - linux_loader::loader::BzImage::load(...) + // - calcular/usar cmdline_addr conforme protocolo + // - setar registradores da vCPU conforme retorno do loader ou layout esperado let mut kernel_image = std::fs::File::open(kernel_path)?; let k = Elf::load(gm, None, &mut kernel_image, None)?; @@ -17,8 +20,11 @@ pub fn load_kernel(gm: &GuestMemoryMmap, kernel_path: &Path, cmdline: &str) -> R // Cmdline let mut cmd = Cmdline::new(1024)?; cmd.insert_str(cmdline)?; - // TODO: alocar e escrever cmdline em memória convidada - let cmdline_addr = GuestAddress(0x20000); // placeholder + // Escrever cmdline NUL-terminada na memória convidada + let cstr = cmd.as_cstring()?; + // TODO(boot): calcular cmdline_addr conforme layout real (abaixo de 1MiB, alinhamento conforme protocolo) + let cmdline_addr = GuestAddress(0x20000); + gm.write_slice(cstr.to_bytes_with_nul(), cmdline_addr)?; Ok(BootInfo { entry: k.kernel_load, cmdline_addr }) diff --git a/src/vmm/devices.rs b/src/vmm/devices.rs index 2277e55..b6989e3 100644 --- a/src/vmm/devices.rs +++ b/src/vmm/devices.rs @@ -12,7 +12,17 @@ pub struct DevicesCfg { pub fn attach_devices(_cfg: &DevicesCfg) -> Result<()> { - // TODO: instanciar vm_superio::Serial (ttyS0) e mapear em PIO/MMIO - // TODO: instanciar virtio-blk (rootfs/workspace) e registrar no event-manager + // TODO(devices/phase1): implementar console via KVM_EXIT_IO (porta 0x3f8) no loop do vCPU + // - Capturar writes no porto THR (0x3f8) e acumular em stdout. + // - Sem device model completo, suficiente para logs do guest. + + // TODO(devices/phase2): usar vm_superio::serial::Serial e despachar PIO para o device + // - Registrar no event loop e conectar a um Write do host para stdout. + // - Ref: https://docs.rs/vm-superio/latest/vm_superio/serial/struct.Serial.html + + // TODO(block): virtio-blk (vda=rootfs RO, vdb=workspace RW) + // - Adicionar dependência do crate virtio-blk (repo vm-virtio) quando implementar. + // - Registrar queues/eventfds no event-manager. + // - Ref: https://github.com/rust-vmm/vm-virtio Ok(()) } \ No newline at end of file diff --git a/src/vmm/event_loop.rs b/src/vmm/event_loop.rs index 1ba3c0f..0d82616 100644 --- a/src/vmm/event_loop.rs +++ b/src/vmm/event_loop.rs @@ -7,7 +7,16 @@ pub struct VmLoop; impl VmLoop { pub fn new() -> Result { Ok(Self) } pub fn run_until_exit(&mut self, _timeout_ms: Option) -> Result { - // TODO: dirigir o loop com event-manager + // TODO(loop/phase1): loop de execução do vCPU usando kvm_run + // - chamar vcpu.run() em ciclo + // - tratar KVM_EXIT_IO (porta 0x3f8) → stdout + // - tratar KVM_EXIT_HLT → encerrar com status 0 + // - tratar falhas (FAIL_ENTRY/SHUTDOWN) → erro ou status + // - respeitar timeout_ms + + // TODO(loop/phase2): substituir por event-manager + // - registrar serial/virtio-blk como subscribers + // - integrar I/O e interrupções conforme backend Ok(0) } } \ No newline at end of file diff --git a/src/vmm/kvm_ctx.rs b/src/vmm/kvm_ctx.rs index ea61485..bcfa59d 100644 --- a/src/vmm/kvm_ctx.rs +++ b/src/vmm/kvm_ctx.rs @@ -13,8 +13,17 @@ impl KvmContext { pub fn new(cpus: u8) -> Result { let kvm = Kvm::new()?; let vm = kvm.create_vm()?; + // TODO(kvm): registrar regiões de memória do convidado no KVM (set_user_memory_region) + // - Iterar GuestMemoryMmap no call-site (run.rs) ou expor API aqui para registrar. + // - Ref: https://docs.rs/kvm-ioctls/latest/kvm_ioctls/struct.VmFd.html#method.set_user_memory_region + let mut vcpus = Vec::new(); - for _i in 0..cpus { vcpus.push(vm.create_vcpu(0)?); /* TODO: regs/sregs/APIC por vCPU */ } + for i in 0..cpus { vcpus.push(vm.create_vcpu(i.into())?); /* TODO: regs/sregs/APIC por vCPU */ } + // TODO(vcpu): configurar CPUID suportado, MSRs, SREGs e REGs + // - get_supported_cpuid → set_cpuid2 + // - get_sregs → ajustar (modo de operação) → set_sregs + // - set_regs conforme loader (RIP/RSP/RFLAGS) + // - Ref: https://docs.rs/kvm-ioctls/latest/kvm_ioctls/struct.VcpuFd.html // TODO: IRQ routing, pit/clk mínimos se necessário Ok(Self { kvm, vm, vcpus }) }} diff --git a/src/vmm/memory.rs b/src/vmm/memory.rs index eec0c6f..c8c0529 100644 --- a/src/vmm/memory.rs +++ b/src/vmm/memory.rs @@ -7,8 +7,9 @@ pub struct GuestMem { pub mem: GuestMemoryMmap } impl GuestMem { pub fn create(mem_mb: u32) -> Result { - let size_u64 = (mem_mb as u64) * 1024 * 1024; - let size: usize = size_u64.try_into().map_err(|_| anyhow::anyhow!("mem size too big"))?; + let size: usize = (mem_mb as usize) + .checked_mul(1024 * 1024) + .ok_or_else(|| anyhow::anyhow!("mem_mb too large"))?; let gm = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), size)])?; Ok(Self { mem: gm }) }} \ No newline at end of file diff --git a/src/vmm/run.rs b/src/vmm/run.rs index c8c59a0..9d16d84 100644 --- a/src/vmm/run.rs +++ b/src/vmm/run.rs @@ -8,6 +8,7 @@ pub fn run_vm(image: &ImageHandle, code: &str, opts: &RunOptions) -> Result Result Result