diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml
index c9ce872fd..71b06b93c 100644
--- a/.github/workflows/gh-pages.yml
+++ b/.github/workflows/gh-pages.yml
@@ -6,23 +6,42 @@ on:
types: [completed]
branches: [master]
+permissions:
+ contents: read
+
jobs:
build:
runs-on: ubuntu-latest
- if: ${{ github.event.repository.fork == false && github.event.workflow_run.conclusion == 'success' }}
+ timeout-minutes: 10
+ if: >-
+ vars.ASTRO_PUBLISH_ENABLED != 'true' &&
+ github.repository == 'LOLBAS-Project/LOLBAS' &&
+ github.event.repository.fork == false &&
+ github.event.workflow_run.conclusion == 'success' &&
+ github.event.workflow_run.head_branch == 'master' &&
+ github.event.workflow_run.event == 'push'
+ concurrency:
+ group: lolbas-external-site-publish
+ cancel-in-progress: false
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.workflow_run.head_sha }}
+ persist-credentials: false
- name: Change .yml to .md
run: |
- for x in $(find yml/ -name '*.yml'); do echo "---" >> "$x"; mv "$x" "${x/%\.yml/.md}"; done
+ while IFS= read -r -d '' x; do
+ echo "---" >> "$x"
+ mv "$x" "${x/%.yml/.md}"
+ done < <(find yml/ -name '*.yml' -print0)
mv yml/OSBinaries yml/Binaries
mv yml/OSLibraries yml/Libraries
mv yml/OSScripts yml/Scripts
rm -r yml/HonorableMentions
- name: Deploy to LOLBAS-Project.github.io repo
- uses: peaceiris/actions-gh-pages@v3
+ uses: peaceiris/actions-gh-pages@v4
with:
deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
external_repository: LOLBAS-Project/LOLBAS-Project.github.io
diff --git a/.github/workflows/site.yml b/.github/workflows/site.yml
new file mode 100644
index 000000000..93d4c1056
--- /dev/null
+++ b/.github/workflows/site.yml
@@ -0,0 +1,129 @@
+name: Validate and publish Astro site
+
+on:
+ push:
+ branches: ['**']
+ pull_request:
+ branches: [master]
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+# PR validation cannot cancel a production build or access the deploy key.
+concurrency:
+ group: astro-site-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: false
+
+jobs:
+ build:
+ # Fork branches can produce review artifacts; upstream validates master/PRs.
+ if: >-
+ github.event_name != 'push' ||
+ github.repository != 'LOLBAS-Project/LOLBAS' ||
+ github.ref == 'refs/heads/master'
+ runs-on: ubuntu-latest
+ timeout-minutes: 20
+ defaults:
+ run:
+ working-directory: website
+ env:
+ ASTRO_TELEMETRY_DISABLED: '1'
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ persist-credentials: false
+ - uses: actions/setup-python@v5
+ with:
+ python-version: '3.12'
+ - name: Install canonical catalog validators
+ working-directory: .
+ run: python -m pip install yamllint==1.37.1 pydantic==2.11.9 PyYAML==6.0.3
+ - name: Lint catalog YAML
+ working-directory: .
+ run: yamllint -c .github/.yamllint yml/**/
+ - name: Validate catalog schemas
+ working-directory: .
+ run: python .github/workflows/validation.py
+ - uses: actions/setup-node@v4
+ with:
+ node-version: '22.22.0'
+ cache: npm
+ cache-dependency-path: website/package-lock.json
+ - run: npm ci --no-audit --no-fund
+ - name: Check source formatting
+ run: npm run format:check
+ - name: Check source and generated data
+ run: npm run check
+ - name: Test data compatibility
+ run: npm run test:data
+ - name: Build and verify static output
+ run: npm run build
+ - name: Record artifact source
+ run: printf '%s\n' "$GITHUB_SHA" > dist/source-revision.txt
+ - run: npx playwright install --with-deps chromium
+ - name: Test the production artifact
+ run: npm test
+ - name: Upload browser evidence
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: browser-evidence-${{ github.run_id }}-${{ github.run_attempt }}
+ path: |
+ website/playwright-report/
+ website/test-results/
+ if-no-files-found: ignore
+ retention-days: 14
+ - name: Upload verified site
+ uses: actions/upload-artifact@v4
+ with:
+ name: verified-site-${{ github.sha }}-${{ github.run_attempt }}
+ path: website/dist/
+ if-no-files-found: error
+ retention-days: 14
+ - name: Summarize review artifacts
+ run: |
+ {
+ printf '### Verified LOLBAS site\n\n'
+ printf 'Source: **%s**\n\n' "$GITHUB_SHA"
+ printf 'Download **verified-site-%s-%s** from this run to preview the tested site locally.\n\n' "$GITHUB_SHA" "$GITHUB_RUN_ATTEMPT"
+ printf 'Inside the extracted artifact, run:\n\n python3 -m http.server 8000\n\nThen open http://localhost:8000/.\n\n'
+ printf 'Browser screenshots and test results are in **browser-evidence-%s-%s**.\n\n' "$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT"
+ printf 'Fork and pull-request runs build review artifacts only. Production publication requires a separate upstream cutover.\n'
+ } >> "$GITHUB_STEP_SUMMARY"
+
+ deploy:
+ needs: build
+ if: >-
+ vars.ASTRO_PUBLISH_ENABLED == 'true' &&
+ github.repository == 'LOLBAS-Project/LOLBAS' &&
+ github.ref == 'refs/heads/master' &&
+ (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ environment:
+ name: github-pages
+ url: https://lolbas-project.github.io
+ concurrency:
+ group: lolbas-external-site-publish
+ cancel-in-progress: false
+ steps:
+ # No checkout or rebuild: publish only this run's tested artifact.
+ - uses: actions/download-artifact@v4
+ with:
+ name: verified-site-${{ github.sha }}-${{ github.run_attempt }}
+ path: verified-site
+ - name: Verify artifact source
+ run: test "$(cat verified-site/source-revision.txt)" = "$GITHUB_SHA"
+ - name: Publish verified Astro site to external repository
+ uses: peaceiris/actions-gh-pages@v4
+ with:
+ deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
+ external_repository: LOLBAS-Project/LOLBAS-Project.github.io
+ publish_branch: master
+ publish_dir: verified-site
+ enable_jekyll: false
+ keep_files: false
+ commit_message: 'Publish verified Astro site from ${{ github.sha }}'
+ user_name: 'github-actions[bot]'
+ user_email: 'github-actions[bot]@users.noreply.github.com'
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..b37cf5cad
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+.context/
+website/node_modules/
+website/dist/
+website/.astro/
+website/src/data/
+website/public/api/
+website/public/assets/lolbas-count.svg
+website/public/mitre_attack_navigator_layer.json
+website/test-results/
+website/playwright-report/
diff --git a/README.md b/README.md
index 813867675..1f8059ac5 100644
--- a/README.md
+++ b/README.md
@@ -55,6 +55,23 @@ If you have found a new LOLBin or LOLScript that you would like to contribute, p
A template for the required format has been provided here: https://github.com/LOLBAS-Project/LOLBAS/blob/master/YML-Template.yml
+## Build the website locally
+
+The Astro website builds from this repository's YAML catalog. With Node.js
+22.22.0 installed, run:
+
+```sh
+cd website
+npm ci
+npm run check
+npm run test:data
+npm run build
+npm run preview -- --port 4387
+```
+
+See [the website guide](website/README.md) for browser tests, development commands,
+and the maintainer-controlled deployment cutover and rollback procedure.
+
## The History of the LOLBin
The phrase "Living off the land" was coined by Christopher Campbell (@obscuresec) & Matt Graeber (@mattifestation) at [DerbyCon 3](https://www.youtube.com/watch?v=j-r6UonEkUw).
diff --git a/docs/ASTRO_CHANGE_CONTRACT.md b/docs/ASTRO_CHANGE_CONTRACT.md
new file mode 100644
index 000000000..b04e1718a
--- /dev/null
+++ b/docs/ASTRO_CHANGE_CONTRACT.md
@@ -0,0 +1,27 @@
+# Astro site migration contract
+
+Implementation, fork push, fork CI build, isolated fork-hosted preview and upstream
+pull request are authorized. Fork preview deployment belongs to a separate
+fork-owned workflow outside the upstream PR.
+Production publication and merging remain separate maintainer actions.
+Canonical content remains `yml/`; source records must not change. Branding is
+LOLBAS only. No analytics, external runtime data dependencies, or backend required.
+
+| Behavior | Acceptance evidence |
+| --- | --- |
+| LOL-SITE-001 | Static catalog includes every legacy-published record; links retain `/lolbas/{type}/{filename}/` and category anchors. |
+| LOL-SITE-002 | Search matches name, descriptions, ATT&CK IDs, command metadata and legacy `/function` / `#type` syntax. Combined category, type, technique, detection filters, column sorting, clear, pagination and URL reload work. |
+| LOL-SITE-003 | Details expose all commands, paths, detections/IOCs, aliases, tags, resources, code samples, author and acknowledgements; copy controls provide honest feedback. |
+| LOL-SITE-004 | Timeline aggregates YAML `Created` dates by year, explains methodology and has accessible chart data and catalog filter links. |
+| LOL-API-001 | JSON retains legacy fields and URLs; CSV retains exact 17-column header and one row per command; Navigator and count badge remain available. Source-derived tests and escaping fixtures. |
+| LOL-CI-001 | Fork branch and PR builds are read-only, cached and tested against production output, producing review artifacts. External publication is opt-in, restricted to upstream master, uses the same tested artifact, and cannot be triggered by forks or PRs. Legacy publication remains until deliberate cutover. |
+
+Assurance: A2 site/API with A3 publication boundary review. Independent review
+required. No authentication boundary exists. Evidence: data tests, Astro check,
+production build verification, desktop/mobile/no-JS/URL/API/console browser checks,
+visual screenshots and independent review. Remote CI/publication not run locally;
+repository settings and external branch backup precede future cutover.
+
+Stop at a fork-hosted branch, verified fork CI build, clickable fork preview and
+upstream PR with screenshots and recorded evidence. Do not merge or publish the
+official LOLBAS site.
diff --git a/docs/screenshots/lolbas-about.png b/docs/screenshots/lolbas-about.png
new file mode 100644
index 000000000..de18a4b64
Binary files /dev/null and b/docs/screenshots/lolbas-about.png differ
diff --git a/docs/screenshots/lolbas-catalog.png b/docs/screenshots/lolbas-catalog.png
new file mode 100644
index 000000000..f7999c492
Binary files /dev/null and b/docs/screenshots/lolbas-catalog.png differ
diff --git a/docs/screenshots/lolbas-detail.png b/docs/screenshots/lolbas-detail.png
new file mode 100644
index 000000000..771676c9d
Binary files /dev/null and b/docs/screenshots/lolbas-detail.png differ
diff --git a/docs/screenshots/lolbas-home.png b/docs/screenshots/lolbas-home.png
new file mode 100644
index 000000000..90580edda
Binary files /dev/null and b/docs/screenshots/lolbas-home.png differ
diff --git a/docs/screenshots/lolbas-mobile.png b/docs/screenshots/lolbas-mobile.png
new file mode 100644
index 000000000..a72df3366
Binary files /dev/null and b/docs/screenshots/lolbas-mobile.png differ
diff --git a/website/.prettierignore b/website/.prettierignore
new file mode 100644
index 000000000..7fbb8bb9f
--- /dev/null
+++ b/website/.prettierignore
@@ -0,0 +1,2 @@
+src/data/
+package-lock.json
diff --git a/website/.prettierrc.json b/website/.prettierrc.json
new file mode 100644
index 000000000..e0087b4ac
--- /dev/null
+++ b/website/.prettierrc.json
@@ -0,0 +1 @@
+{ "plugins": ["prettier-plugin-astro"], "singleQuote": true }
diff --git a/website/README.md b/website/README.md
new file mode 100644
index 000000000..92fef9cdf
--- /dev/null
+++ b/website/README.md
@@ -0,0 +1,121 @@
+# LOLBAS website
+
+The Astro site builds directly from the repository's `yml/` catalog. Edit those
+YAML records to update the catalog; generated data and static output are build
+products. The existing YAML lint and schema workflow remains a separate gate.
+
+## Local development and verification
+
+Use Node.js **22.22.0** and the committed npm lockfile. From the repository root:
+
+```sh
+cd website
+npm ci
+npm run check
+npm run test:data
+npm run build
+npx playwright install chromium
+npm test
+npm run preview -- --port 4387
+```
+
+`check` regenerates catalog data and runs Astro's checks. `test:data` runs the
+data and publication regression tests. `build` regenerates data, produces `dist/`, and verifies
+the generated output. Playwright tests the production build through the preview
+server on port 4388; run `build` before `test`. The live preview uses port 4387
+so other workspaces can keep using the default Astro port. On Linux CI, install browser system dependencies
+with `npx playwright install --with-deps chromium`.
+
+For interactive development, run `npm run dev`. Run `npm run prepare:data` after
+changing catalog YAML during an existing development session. Do not execute the
+catalog's example commands: they are documentation rendered by the site.
+
+## Charts and compatibility
+
+The growth chart groups YAML `Created` dates by year and shows cumulative entries;
+it is catalog growth, not observed threat activity. Function counts count entries
+with a matching command; an entry can have multiple functions. Detection counts
+include published links and exclude IOC-only records.
+
+Legacy JSON fields, CSV columns, detail URLs/category anchors, the Navigator layer
+and count badge remain available. Generated API files do not include UI metadata.
+Search also accepts the legacy `/function` and `#type` syntax. Recently added
+sorting uses the recorded `Created` date; it does not imply last-modified dates.
+
+Every catalog column header toggles ascending/descending order. Function lists
+and ATT&CK lists sort alphabetically and numerically respectively; detections
+sort by published resource count. The dropdown, header indicators and URL share
+one sort state. Older browsers retain native filter menus through progressive CSS.
+
+## CI and publication boundary
+
+`.github/workflows/site.yml` validates all fork branch pushes, upstream `master`
+pushes, pull requests targeting `master`, and manual runs. Fork builds require
+Actions to be enabled in the fork. They upload review artifacts and browser
+evidence without publishing a site or needing deployment secrets. Download the
+verified site from the run and serve the extracted directory with
+`python3 -m http.server 8000` to preview it. It caches npm downloads, installs with `npm ci`, runs
+checks and data tests, builds and verifies the site, and runs Chromium against
+the production output. Only a successful run uploads the `verified-site` artifact
+for publication. `source-revision.txt` records its source SHA. Browser evidence is
+uploaded separately, including on test failure when evidence exists.
+
+The UI also supports an optional Astro `SITE_BASE` (for example `/LOLBAS`) for
+project-site previews. A separate fork-owned workflow can build with that value,
+run the preview smoke test via `SITE_BASE=/LOLBAS npm test`, and publish to the
+fork's Pages site. That preview publisher is intentionally outside this upstream
+change. Canonical API export URLs remain unchanged in prefixed previews.
+
+Publication defaults to **disabled**. The repository Actions variable
+`ASTRO_PUBLISH_ENABLED` must equal the literal string `true`. The publisher also
+requires the canonical `LOLBAS-Project/LOLBAS` repository, the `master` branch,
+and either a push or manual dispatch. Pull requests and `workflow_run` events
+cannot publish Astro output. The `github-pages` environment can require human
+approval. Use a **repository variable**, not an environment variable, because
+both workflow job conditions must see the switch before entering an environment.
+
+The publisher downloads the tested artifact from the same workflow run and
+verifies its recorded SHA. It does not rebuild. Using the existing
+`ACTIONS_DEPLOY_KEY`, it replaces the root of the external repository
+`LOLBAS-Project/LOLBAS-Project.github.io` on branch `master`, with Jekyll disabled.
+This is an external-repository publication, not an `actions/deploy-pages` upload.
+
+While the switch is unset or differs from `true`, the legacy Jekyll catalog
+publisher remains enabled. It accepts only successful YAML-validation runs
+originating from a push, checks out that validated run's exact SHA, and updates
+the external repository's `_lolbas` directory. Both publishers share a deployment
+concurrency group.
+
+## Maintainer cutover and rollback
+
+The following is a human-operated release procedure; local implementation and
+review do not authorize a live publication.
+
+1. Review the migration diff and independent release review, including catalog
+ compatibility, URLs, production browser results, and the generated artifact.
+ Confirm the master revision has passed both site and YAML/schema checks.
+2. Back up the external repository's current `master` Jekyll source to a retained
+ branch or tag and record its commit SHA. Preserve any custom domain, redirects,
+ and other site assets needed after replacing the root. Confirm the external
+ repository's Pages settings serve `master` from the root.
+3. Configure the source repository's `github-pages` environment and desired
+ required reviewers. Confirm the existing deploy key can write only to the
+ intended external repository. Wait for pending legacy publications to finish.
+4. Set the **repository Actions variable** `ASTRO_PUBLISH_ENABLED=true`, then
+ manually run the site workflow on `master`. Review and approve the environment
+ deployment if protection requires it. This publishes that run's tested artifact.
+5. Check the live homepage, a legacy detail URL, search/filter behavior, API
+ exports, and `source-revision.txt`. Retain the tested artifact and deployment
+ evidence with the release record.
+
+To roll back, unset `ASTRO_PUBLISH_ENABLED` (or set it to `false`) and cancel any
+queued or running Astro deployment before restoring the external `master` branch
+to the backed-up Jekyll source. Restore Pages/custom-domain settings if changed.
+Verify the live legacy site, then allow future successful push-triggered YAML
+validation runs to update `_lolbas` again. Merely unsetting the variable does not
+restore the Jekyll files removed by the Astro root publication.
+
+Required release review can be performed locally against workflows and build
+evidence. Live deployment, repository-variable changes, environment changes, and
+rollback operations are skipped during local validation and require a maintainer
+to execute this release procedure.
diff --git a/website/astro.config.mjs b/website/astro.config.mjs
new file mode 100644
index 000000000..c74353d5f
--- /dev/null
+++ b/website/astro.config.mjs
@@ -0,0 +1,8 @@
+import { defineConfig } from 'astro/config';
+export default defineConfig({
+ site: 'https://lolbas-project.github.io',
+ base: process.env.SITE_BASE || '/',
+ output: 'static',
+ trailingSlash: 'always',
+ devToolbar: { enabled: false },
+});
diff --git a/website/package-lock.json b/website/package-lock.json
new file mode 100644
index 000000000..22b42bc45
--- /dev/null
+++ b/website/package-lock.json
@@ -0,0 +1,5457 @@
+{
+ "name": "lolbas-website",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "lolbas-website",
+ "version": "1.0.0",
+ "dependencies": {
+ "astro": "7.3.5",
+ "yaml": "2.9.1"
+ },
+ "devDependencies": {
+ "@astrojs/check": "0.9.10",
+ "@playwright/test": "1.63.0",
+ "@types/node": "22.20.3",
+ "prettier": "3.9.7",
+ "prettier-plugin-astro": "1.0.0",
+ "typescript": "6.0.3"
+ },
+ "engines": {
+ "node": ">=22.22.0"
+ }
+ },
+ "node_modules/@astrojs/astro2tsx": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/@astrojs/astro2tsx/-/astro2tsx-0.1.2.tgz",
+ "integrity": "sha512-Uj/M9zbrhu5cIZEvWAGTbQJRKSxXWJ/E59BZwt7F+chrKhbfttW+9rkS/l+1QxoJP1KU3UnpM62M7yszfAI0WA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@emnapi/core": "1.11.3",
+ "@napi-rs/wasm-runtime": "1.2.4"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/check": {
+ "version": "0.9.10",
+ "resolved": "https://registry.npmjs.org/@astrojs/check/-/check-0.9.10.tgz",
+ "integrity": "sha512-zgx/UQMozdjOa3bOxjgeCFdtpE3c9rRX6xHwa+2QXvy8z8Akifu2AtubHyv/zzC2znO8dl8fFWL4K+Ba9kS8HQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/language-server": "^2.16.7",
+ "chokidar": "^4.0.3",
+ "kleur": "^4.1.5",
+ "yargs": "^18.0.0"
+ },
+ "bin": {
+ "astro-check": "bin/astro-check.js"
+ },
+ "peerDependencies": {
+ "typescript": "^5.0.0 || ^6.0.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding/-/compiler-binding-0.5.1.tgz",
+ "integrity": "sha512-oczyolLUl9mNrtuBuNjuvn5thpHrFLXAHAWGTRg0JDHQ8cGVsVeFekOu2uSoQGgainJIi+A3/9m0DyYwz3Nlnw==",
+ "license": "MIT",
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@astrojs/compiler-binding-android-arm64": "0.5.1",
+ "@astrojs/compiler-binding-darwin-arm64": "0.5.1",
+ "@astrojs/compiler-binding-darwin-x64": "0.5.1",
+ "@astrojs/compiler-binding-linux-arm64-gnu": "0.5.1",
+ "@astrojs/compiler-binding-linux-arm64-musl": "0.5.1",
+ "@astrojs/compiler-binding-linux-x64-gnu": "0.5.1",
+ "@astrojs/compiler-binding-linux-x64-musl": "0.5.1",
+ "@astrojs/compiler-binding-wasm32-wasi": "0.5.1",
+ "@astrojs/compiler-binding-win32-arm64-msvc": "0.5.1",
+ "@astrojs/compiler-binding-win32-x64-msvc": "0.5.1"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-android-arm64": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-android-arm64/-/compiler-binding-android-arm64-0.5.1.tgz",
+ "integrity": "sha512-nMb2nMIJtcWLNH+fV75Enmi+jp4tIOYOfsv4XtpgdOWkqc9P4uSSdK5u1xb340AzrXo5V18SxLl/nc3vcqtamQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-darwin-arm64": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-arm64/-/compiler-binding-darwin-arm64-0.5.1.tgz",
+ "integrity": "sha512-5k4+nZ/WRt+dcVow4z7rBuI7iv9A58h/uQ91YJe1PrTivbcCkq3u2Ynx/AZJwII4CsZawBCeGpojkFjutZBhBQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-darwin-x64": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-x64/-/compiler-binding-darwin-x64-0.5.1.tgz",
+ "integrity": "sha512-0Buw+YST9Ff2EqQyhRMQujuFq/dijLw3zXWhsvI0dJ2pGrT38iVoxTEca2eBX/PfhlwkVWSRJEKO5gMqKHyeHg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-linux-arm64-gnu": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-gnu/-/compiler-binding-linux-arm64-gnu-0.5.1.tgz",
+ "integrity": "sha512-x5LRpyCoMKuu3INr18cn42YhwSOaRpp9b5Ny3eJJ/sWVeejC16Zeshu1JlvlSmEZACLeWxU5JM0RX6T6nI+YyA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-linux-arm64-musl": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-musl/-/compiler-binding-linux-arm64-musl-0.5.1.tgz",
+ "integrity": "sha512-mL7tQEuDk3rGfaKmrO63nw6WBy1TPVioQGmVyjcAr+C+a/k3SVMYBytNSMqH9mZPT/Swj30JaZ2N842Z1NnOEQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-linux-x64-gnu": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-gnu/-/compiler-binding-linux-x64-gnu-0.5.1.tgz",
+ "integrity": "sha512-uLGc9jtxwiDe4cQyhNPQim4ECiB/YzbWlXssfLAv8r374kArfTwBHjwR2+j+XVyF71yCx5mF9YNRclRqdNBgeg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-linux-x64-musl": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-musl/-/compiler-binding-linux-x64-musl-0.5.1.tgz",
+ "integrity": "sha512-G0cywinaQUc8v1MrytvYKUX7svTDMmYtOCZfxjwKPbJi59zIqM+Ll0rWgO0GQWLZH3XoPYo19ZwlP3Xt9ep9YA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-wasm32-wasi": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-wasm32-wasi/-/compiler-binding-wasm32-wasi-0.5.1.tgz",
+ "integrity": "sha512-yMhLL8asmLydoAdnEZxAw6eLfNDs3YJl6jqn0pjPt6zrb2U5qIDLnii4/twThpIFODKkQ6YDpBRTOz/vSfZCxw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "1.11.3",
+ "@emnapi/runtime": "1.11.3",
+ "@napi-rs/wasm-runtime": "^1.2.4"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-win32-arm64-msvc": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-arm64-msvc/-/compiler-binding-win32-arm64-msvc-0.5.1.tgz",
+ "integrity": "sha512-aClIG6poFt3Wf/bZykrEAX2nAnKvBHW6RXiKUa69QjBtWH0/FOlOAjnFcxRNlhej2AMWNYFx0o+LGjwMcH8P7Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-win32-x64-msvc": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-x64-msvc/-/compiler-binding-win32-x64-msvc-0.5.1.tgz",
+ "integrity": "sha512-/E6uBCagY70YpWqssBC+/FuniNSloeQPRgj7l3LDWiqjxIyTLnIgX1sTt18FU7jrigetpSnC9cHAM6b6IxSgPw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-rs": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-rs/-/compiler-rs-0.5.1.tgz",
+ "integrity": "sha512-1k1acnX7DepIiea9U9/Y+d1hdAFfylu1ZQQkkYgRSwBOBAsXInolyZuh/2tax+83vu/A+hOYP6P3+265RvMTvw==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/compiler-binding": "0.5.1"
+ },
+ "engines": {
+ "node": ">=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/internal-helpers": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.11.0.tgz",
+ "integrity": "sha512-3rzxJ+xbo0+8YyqOzLziIN32wmsHdCjEVz2sGOpRxJ+Ben/KiLph4ItxBy1abEL+E8fkRzqjg0rfXmaHJGw9JA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.4",
+ "@types/mdast": "^4.0.4",
+ "js-yaml": "^4.3.0",
+ "picomatch": "^4.0.4",
+ "retext-smartypants": "^6.2.0",
+ "shiki": "^4.0.2",
+ "smol-toml": "^1.6.0",
+ "unified": "^11.0.5"
+ }
+ },
+ "node_modules/@astrojs/language-server": {
+ "version": "2.17.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/language-server/-/language-server-2.17.1.tgz",
+ "integrity": "sha512-gG7bvMVNIXgoSe1pM9dI1HsSDLU1EIc/dSAYQ0DuReLyLkOBjsiqvaRYgQltWaldLxh0bNEqjgreLfjd9OnUxQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/astro2tsx": "^0.1.0",
+ "@astrojs/yaml2ts": "^0.2.4",
+ "@volar/kit": "~2.4.28",
+ "@volar/language-core": "~2.4.28",
+ "@volar/language-server": "~2.4.28",
+ "@volar/language-service": "~2.4.28",
+ "muggle-string": "^0.4.1",
+ "tinyglobby": "^0.2.17",
+ "volar-service-css": "0.0.71",
+ "volar-service-emmet": "0.0.71",
+ "volar-service-html": "0.0.71",
+ "volar-service-prettier": "0.0.71",
+ "volar-service-typescript": "0.0.71",
+ "volar-service-typescript-twoslash-queries": "0.0.71",
+ "volar-service-yaml": "0.0.71",
+ "vscode-html-languageservice": "^5.6.2",
+ "vscode-uri": "^3.2.0"
+ },
+ "bin": {
+ "astro-ls": "bin/nodeServer.js"
+ },
+ "peerDependencies": {
+ "prettier": "^3.0.0",
+ "prettier-plugin-astro": ">=0.11.0"
+ },
+ "peerDependenciesMeta": {
+ "prettier": {
+ "optional": true
+ },
+ "prettier-plugin-astro": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@astrojs/markdown-satteri": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@astrojs/markdown-satteri/-/markdown-satteri-0.4.2.tgz",
+ "integrity": "sha512-g4QehXnB/MzxQWnSf17NciFInMqEJy9948p8cc7zGg/r7995m0Ln5PMr3Xlnj24iey1LchaoZ3SzTOa3D4P/3w==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/internal-helpers": "0.11.0",
+ "@astrojs/prism": "4.0.2",
+ "github-slugger": "^2.0.0",
+ "satteri": "^0.10.3"
+ }
+ },
+ "node_modules/@astrojs/prism": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-4.0.2.tgz",
+ "integrity": "sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==",
+ "license": "MIT",
+ "dependencies": {
+ "prismjs": "^1.30.0"
+ },
+ "engines": {
+ "node": ">=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/telemetry": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.3.tgz",
+ "integrity": "sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ci-info": "^4.4.0",
+ "dset": "^3.1.4",
+ "is-docker": "^4.0.0",
+ "package-manager-detector": "^1.6.0"
+ },
+ "engines": {
+ "node": "18.20.8 || ^20.3.0 || >=22.0.0"
+ }
+ },
+ "node_modules/@astrojs/yaml2ts": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/@astrojs/yaml2ts/-/yaml2ts-0.2.4.tgz",
+ "integrity": "sha512-8oddpOae35pJsXPQXhTkM0ypfKPskVsh2bCxRtbf7e+/Epw2nReakFYpLKjZMEr75CsoF203PMnCocpfz0s69A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yaml": "^2.8.3"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.9",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz",
+ "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.8"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz",
+ "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@bruits/satteri-darwin-arm64": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-arm64/-/satteri-darwin-arm64-0.10.5.tgz",
+ "integrity": "sha512-27KTVl4TJkVahMy/ohyA7qd4938G5UNneFUz/PsScYfpIhj0IVAS23mpcJXdPF44sa6nva198lmV/cKIb2YPyA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@bruits/satteri-darwin-x64": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-x64/-/satteri-darwin-x64-0.10.5.tgz",
+ "integrity": "sha512-IjnLe3nKspq6qaeqGgjT7MT8VrTV74yWRlaag7ZdNsI8TDAYZ0iPxMCo+9KQZHUk5EyVB+reBI/PFWL5KuFw9Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@bruits/satteri-linux-arm64-gnu": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-gnu/-/satteri-linux-arm64-gnu-0.10.5.tgz",
+ "integrity": "sha512-glkYXZCJywjP13v67eAyAMSJdF+ncvEbYvgi/wOtffL9tQ27lr/zsyzUfgs+ovjJ9d8JNQKiXeiArJcX8PJL9w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@bruits/satteri-linux-arm64-musl": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-musl/-/satteri-linux-arm64-musl-0.10.5.tgz",
+ "integrity": "sha512-yWdgG1g17Nh2QyGVlFUxGRa3FEFwiMcpZEyMNWkbM3deC94cmVc+/i9OuyFpdKuWo3GkgoCtYVOoxk1uCnCZIA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@bruits/satteri-linux-x64-gnu": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-gnu/-/satteri-linux-x64-gnu-0.10.5.tgz",
+ "integrity": "sha512-FVaLoPT1fBgGl0J+AYebyyXJYBachGl8Oyyrf1lye4RTqCB4S0Gwkj1uM9RJyThUOvx5VUmAT1CnNh1SFHA+kw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@bruits/satteri-linux-x64-musl": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-musl/-/satteri-linux-x64-musl-0.10.5.tgz",
+ "integrity": "sha512-EHpVAx2bqW3GINHTKkljtxVfQmVDGWIuwOYOP5YghTj+0PkBa2o8oKPRtQ9Kbsr1Fye8jtUcDjhwj2jMNugZKg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@bruits/satteri-wasm32-wasi": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@bruits/satteri-wasm32-wasi/-/satteri-wasm32-wasi-0.10.5.tgz",
+ "integrity": "sha512-ypz8c/Zmipxp4IoeDa228Gstv6TLzVmNs3yC6wKCoNSOjx1iwpgzu87Y3hTkXFdwChVGU85qeUDuOIarGUZQLw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "1.11.1",
+ "@emnapi/runtime": "1.11.1",
+ "@napi-rs/wasm-runtime": "^1.2.3"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/core": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz",
+ "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.2",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/runtime": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz",
+ "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
+ "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@bruits/satteri-win32-arm64-msvc": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-arm64-msvc/-/satteri-win32-arm64-msvc-0.10.5.tgz",
+ "integrity": "sha512-siTV88nb0LRqNpkL2gXboqCwVdq95sLtzMHS1/3eONV2gLbB3NAK46wmSMvCO/yquBvI2lvaFIfd8P12ecsxBw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@bruits/satteri-win32-x64-msvc": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-x64-msvc/-/satteri-win32-x64-msvc-0.10.5.tgz",
+ "integrity": "sha512-C3IfPvfvMXmlzBxaMPKFS1XiuV9pu2mC7YqkPk7PSvTgPZ8gbdASIpHpztDLvTTQjqZ0z1Ol8tK5X+V6XXC0wQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@capsizecss/unpack": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.1.tgz",
+ "integrity": "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "fontkitten": "^1.0.3"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@clack/core": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.5.1.tgz",
+ "integrity": "sha512-iHTrHA8MtVuLl2TfZySmcKv1qO2PoyC9Z7pfSDozEuV5vtY3/wcOPKJXlqJ5Oq2Cx5DDGQGAMVx6HZfRRoVEbQ==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-wrap-ansi": "^0.2.0",
+ "sisteransi": "^1.0.5"
+ },
+ "engines": {
+ "node": ">= 20.12.0"
+ }
+ },
+ "node_modules/@clack/prompts": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.8.1.tgz",
+ "integrity": "sha512-dlT1m5e/0yUL0kRNcQn7yGLVThkgbB0Ga/1AmfDDC/8ik6AIiSf2QLQO2zPYvefsHP0aFgxO93cVLCCfDp7kzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@clack/core": "1.5.1",
+ "fast-string-width": "^3.0.2",
+ "fast-wrap-ansi": "^0.2.0",
+ "sisteransi": "^1.0.5"
+ },
+ "engines": {
+ "node": ">= 20.12.0"
+ }
+ },
+ "node_modules/@emmetio/abbreviation": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz",
+ "integrity": "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@emmetio/scanner": "^1.0.4"
+ }
+ },
+ "node_modules/@emmetio/css-abbreviation": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/@emmetio/css-abbreviation/-/css-abbreviation-2.1.8.tgz",
+ "integrity": "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@emmetio/scanner": "^1.0.4"
+ }
+ },
+ "node_modules/@emmetio/css-parser": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@emmetio/css-parser/-/css-parser-0.4.1.tgz",
+ "integrity": "sha512-2bC6m0MV/voF4CTZiAbG5MWKbq5EBmDPKu9Sb7s7nVcEzNQlrZP6mFFFlIaISM8X6514H9shWMme1fCm8cWAfQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@emmetio/stream-reader": "^2.2.0",
+ "@emmetio/stream-reader-utils": "^0.1.0"
+ }
+ },
+ "node_modules/@emmetio/html-matcher": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@emmetio/html-matcher/-/html-matcher-1.3.0.tgz",
+ "integrity": "sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@emmetio/scanner": "^1.0.0"
+ }
+ },
+ "node_modules/@emmetio/scanner": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@emmetio/scanner/-/scanner-1.0.4.tgz",
+ "integrity": "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@emmetio/stream-reader": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@emmetio/stream-reader/-/stream-reader-2.2.0.tgz",
+ "integrity": "sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@emmetio/stream-reader-utils": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@emmetio/stream-reader-utils/-/stream-reader-utils-0.1.0.tgz",
+ "integrity": "sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@emnapi/core": {
+ "version": "1.11.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz",
+ "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.3",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.11.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz",
+ "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz",
+ "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz",
+ "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz",
+ "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz",
+ "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz",
+ "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz",
+ "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz",
+ "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz",
+ "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz",
+ "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz",
+ "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz",
+ "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz",
+ "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz",
+ "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz",
+ "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz",
+ "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz",
+ "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz",
+ "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz",
+ "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz",
+ "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz",
+ "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz",
+ "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz",
+ "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz",
+ "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz",
+ "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@img/colour": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
+ "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.35.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.5.tgz",
+ "integrity": "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.3.4"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.35.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.5.tgz",
+ "integrity": "sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.3.4"
+ }
+ },
+ "node_modules/@img/sharp-freebsd-wasm32": {
+ "version": "0.35.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.5.tgz",
+ "integrity": "sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "dependencies": {
+ "@img/sharp-wasm32": "0.35.5"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.4.tgz",
+ "integrity": "sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==",
+ "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.3.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.4.tgz",
+ "integrity": "sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==",
+ "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.3.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.4.tgz",
+ "integrity": "sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==",
+ "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.3.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.4.tgz",
+ "integrity": "sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.4.tgz",
+ "integrity": "sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.4.tgz",
+ "integrity": "sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==",
+ "cpu": [
+ "riscv64"
+ ],
+ "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.3.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.4.tgz",
+ "integrity": "sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==",
+ "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.3.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.4.tgz",
+ "integrity": "sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==",
+ "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.3.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.4.tgz",
+ "integrity": "sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==",
+ "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.3.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.4.tgz",
+ "integrity": "sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==",
+ "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.35.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.5.tgz",
+ "integrity": "sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.3.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.35.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.5.tgz",
+ "integrity": "sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.3.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.35.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.5.tgz",
+ "integrity": "sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.3.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.35.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.5.tgz",
+ "integrity": "sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.3.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.35.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.5.tgz",
+ "integrity": "sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.3.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.35.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.5.tgz",
+ "integrity": "sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.3.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.35.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.5.tgz",
+ "integrity": "sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.3.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.35.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.5.tgz",
+ "integrity": "sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.3.4"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.35.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.5.tgz",
+ "integrity": "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==",
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.11.3"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-webcontainers-wasm32": {
+ "version": "0.35.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.5.tgz",
+ "integrity": "sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@img/sharp-wasm32": "0.35.5"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.35.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.5.tgz",
+ "integrity": "sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.35.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.5.tgz",
+ "integrity": "sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.35.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.5.tgz",
+ "integrity": "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz",
+ "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==",
+ "license": "MIT"
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.4.tgz",
+ "integrity": "sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tybys/wasm-util": "^0.10.3"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=23.5.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4",
+ "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4"
+ }
+ },
+ "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/@oxc-project/types": {
+ "version": "0.152.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.152.0.tgz",
+ "integrity": "sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/oxc-project"
+ }
+ },
+ "node_modules/@playwright/test": {
+ "version": "1.63.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.63.0.tgz",
+ "integrity": "sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright": "1.63.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@prettier/parse-srcset": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@prettier/parse-srcset/-/parse-srcset-3.1.0.tgz",
+ "integrity": "sha512-FIRv2rZotO9NP/r66taYqD1zICpPiBbrECqjKV/xqNotqDxF7fLzzUeK+1RSRx9Tenk0DajR/GcRmTJ2qtHaKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rolldown/binding-android-arm-eabi": {
+ "version": "1.2.12",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.12.tgz",
+ "integrity": "sha512-dB/a1214qKfHMXCpgqR4OZT+jS4kTyEXbQGJPqzobt5EwH5rX080pxE37alt3RzvR1bf1Yz/yGqRfrYAxuPw0A==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.2.12",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.12.tgz",
+ "integrity": "sha512-7KHFgQ5VJxIHcLlrwrc3Xbds7oTNQT7Pgi9gQCJKrd2VGab/UksIOYp6VD8MzCstGxOKMgNamPwUCfxPdP1OHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.2.12",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.12.tgz",
+ "integrity": "sha512-3YIhqHD96nA5SaYNRBR16HnGv4oavZvXfD/ayHM+oYZ0WD/8lBAtf6zQua4kEyAvpqrluKXl0lnOBoiNby7x9w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.2.12",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.12.tgz",
+ "integrity": "sha512-UuuJ35MFw4gmFOrE9pEqIV+K3syIKveph+Qc1/ljHZVdoDW4pz/JHR/eMVom+TZGl/5OOvGJOWaOCVt3ZfqhxA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.2.12",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.12.tgz",
+ "integrity": "sha512-uMvssit0a4W+/7D8CbHUvG719mH3R2jwXAlh/XcPvuHTE0g++LymF88DCGNX0HM2rBOn0xrzgXktIB6fLSJBTQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.2.12",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.12.tgz",
+ "integrity": "sha512-XcFu0R0xWnwzSf4IQgFH1rJIckPN1pLy2R+4r9IDB7Yfu/ys9cVqfa4pBrMHj7a3gl8mIR4nRNPg0e5IvEVs6g==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.2.12",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.12.tgz",
+ "integrity": "sha512-260UrKgn8tz39ak+SMDOirKzr7V04M9dWPw5llW00SwBivCZoWcRBKV1d8cXnRkUmSZA3BdiUmBHWk7734Ulpw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.2.12",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.12.tgz",
+ "integrity": "sha512-5YK1I9SqDkbPgc1IA8BgDl34suqUS2q0KWnBrirm0E51YjOs6eo6dV6jbQfNE/argHRSvd0QUGgtpIoYx+WWpw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.2.12",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.12.tgz",
+ "integrity": "sha512-Rkcrmp7eFRg74yL5fXEU91JEWbdEPLevWwGtXpmhbjlD1StScbWTmO94Bhly+Mo+ketKYkdmM1vNUKeWSlx8cQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.2.12",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.12.tgz",
+ "integrity": "sha512-qvK4DuAsQc2BSjlx+Xr+IzOIvvxbGZqxFwdWfG6F518Erj0GGISyQbJ6pIappnOxlNPzNHvo/L0BwB30GZ+zVw==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.2.12",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.12.tgz",
+ "integrity": "sha512-Q9uLBO53Xd4QIq1WOycVQyPP1O4HhraEV2qqb3uTrnVw6QZih9duY4vNXOivL1xoUS1/z+W8eF4NMfl2a8Sdjw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.2.12",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.12.tgz",
+ "integrity": "sha512-3IBxWFMjbOZskDPKv8Lf9BCnahlKuHthWkYnyIxOH/QcJrFcS4EmcenthApkwr/5+nEqZlLzeYbxeMaX7A5u4g==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.2.12",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.12.tgz",
+ "integrity": "sha512-xtX61xg4LKPkPWilZU1ynKClz5Gj4bf74LML4r3eVLWumKnGjoEr1OSHQhMdbBDoYTi+yjrujvpZe2pUnqCrrA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.2.12",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.12.tgz",
+ "integrity": "sha512-At7fPB6PCaIjzgIhEZFxuT+BBFqiQibJDT4d3PhiR3f4E7bbMZF4aKblbFfEM3sETRDd1YiQx/+U/g/B/ou5Ew==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.2.12",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.12.tgz",
+ "integrity": "sha512-WIw2haVKwjuYdXkHaoC0mF8Le71TuCBxjrdKqLbJGctbBABj+ClfmNvtbOnzpq3RokNo5+V1qhtSzJyXorsklQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
+ "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
+ "license": "MIT"
+ },
+ "node_modules/@shikijs/core": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.5.0.tgz",
+ "integrity": "sha512-6CIgwq+bt2JZ3OSjb/nBkkwEf/CfpHcbam+Mg5Pllco7HAGRQ2/EFhZrrJtbyNTgKwknfVS+llsXGygiDGwJWw==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/primitive": "4.5.0",
+ "@shikijs/types": "4.5.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.5",
+ "hast-util-to-html": "^9.0.5"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/engine-javascript": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.5.0.tgz",
+ "integrity": "sha512-Ed6UdEj84LyYkwnxM5CvSMWAD+id9ExTYri0/+x41U9zrgNifPSS9lZm0JqBP0VfYI8yr8tY5nbNq9+p9R0kFA==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "4.5.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "oniguruma-to-es": "^4.3.6"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/engine-oniguruma": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.5.0.tgz",
+ "integrity": "sha512-dKVmbJecVdB9recl8a/CU4jdpNXyjJQrIcesIkpS70s5HS02ohpnCSpMUbotPnEUzbAcLQ+S4hwZYbkBBiTcJw==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "4.5.0",
+ "@shikijs/vscode-textmate": "^10.0.2"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/langs": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.5.0.tgz",
+ "integrity": "sha512-wwBTqwduXSz0X4v9Qa2EWeFaYJEw1clCncWjoRNQRbVDu3SPRTRWucnouYapKj3b8Q+sk/41Vbc4B5c8nuYzLA==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "4.5.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/primitive": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.5.0.tgz",
+ "integrity": "sha512-UM7ShlA8oz0OWxLLO8BmO9Ip6fVqdkt8yF0EH7nOCqHfrx1eEAzF0ZUVK7L+I8NEgeu1PEWSRlxaiPPeCb4iDg==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "4.5.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/themes": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.5.0.tgz",
+ "integrity": "sha512-zGb87H3UBbpuI4yoh5UguB1XbU+2BDU3kflM7UfZ+Cy2hWo9lBtqjSVK20gmLY6ukYl040hAypGXMgud9vul8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "4.5.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/types": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.5.0.tgz",
+ "integrity": "sha512-drtPrtD8N0DENyD2cyt6Ajy+IWsiCLgMxODwLTpvH1UsN6a9jf90tNTPjqwHvA+0Lzu9e0txk8DjfqH1p9UJRw==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "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/@tybys/wasm-util": {
+ "version": "0.10.4",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.4.tgz",
+ "integrity": "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "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/hast": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz",
+ "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "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/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": "22.20.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.3.tgz",
+ "integrity": "sha512-DZmzkmwHzXrLPAXPyKNDzlIwMMUZCVacoD25ywdy5YTKGbOx/2ld+Q38Im2zJ0vBuZP5Prd3VZutKZyXwkOS8A==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "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.4.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz",
+ "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==",
+ "license": "ISC"
+ },
+ "node_modules/@volar/kit": {
+ "version": "2.4.28",
+ "resolved": "https://registry.npmjs.org/@volar/kit/-/kit-2.4.28.tgz",
+ "integrity": "sha512-cKX4vK9dtZvDRaAzeoUdaAJEew6IdxHNCRrdp5Kvcl6zZOqb6jTOfk3kXkIkG3T7oTFXguEMt5+9ptyqYR84Pg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/language-service": "2.4.28",
+ "@volar/typescript": "2.4.28",
+ "typesafe-path": "^0.2.2",
+ "vscode-languageserver-textdocument": "^1.0.11",
+ "vscode-uri": "^3.0.8"
+ },
+ "peerDependencies": {
+ "typescript": "*"
+ }
+ },
+ "node_modules/@volar/language-core": {
+ "version": "2.4.28",
+ "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz",
+ "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/source-map": "2.4.28"
+ }
+ },
+ "node_modules/@volar/language-server": {
+ "version": "2.4.28",
+ "resolved": "https://registry.npmjs.org/@volar/language-server/-/language-server-2.4.28.tgz",
+ "integrity": "sha512-NqcLnE5gERKuS4PUFwlhMxf6vqYo7hXtbMFbViXcbVkbZ905AIVWhnSo0ZNBC2V127H1/2zP7RvVOVnyITFfBw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/language-core": "2.4.28",
+ "@volar/language-service": "2.4.28",
+ "@volar/typescript": "2.4.28",
+ "path-browserify": "^1.0.1",
+ "request-light": "^0.7.0",
+ "vscode-languageserver": "^9.0.1",
+ "vscode-languageserver-protocol": "^3.17.5",
+ "vscode-languageserver-textdocument": "^1.0.11",
+ "vscode-uri": "^3.0.8"
+ }
+ },
+ "node_modules/@volar/language-service": {
+ "version": "2.4.28",
+ "resolved": "https://registry.npmjs.org/@volar/language-service/-/language-service-2.4.28.tgz",
+ "integrity": "sha512-Rh/wYCZJrI5vCwMk9xyw/Z+MsWxlJY1rmMZPsxUoJKfzIRjS/NF1NmnuEcrMbEVGja00aVpCsInJfixQTMdvLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/language-core": "2.4.28",
+ "vscode-languageserver-protocol": "^3.17.5",
+ "vscode-languageserver-textdocument": "^1.0.11",
+ "vscode-uri": "^3.0.8"
+ }
+ },
+ "node_modules/@volar/source-map": {
+ "version": "2.4.28",
+ "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz",
+ "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@volar/typescript": {
+ "version": "2.4.28",
+ "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz",
+ "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/language-core": "2.4.28",
+ "path-browserify": "^1.0.1",
+ "vscode-uri": "^3.0.8"
+ }
+ },
+ "node_modules/@vscode/emmet-helper": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/@vscode/emmet-helper/-/emmet-helper-2.11.0.tgz",
+ "integrity": "sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emmet": "^2.4.3",
+ "jsonc-parser": "^2.3.0",
+ "vscode-languageserver-textdocument": "^1.0.1",
+ "vscode-languageserver-types": "^3.15.1",
+ "vscode-uri": "^3.0.8"
+ }
+ },
+ "node_modules/@vscode/emmet-helper/node_modules/jsonc-parser": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz",
+ "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@vscode/l10n": {
+ "version": "0.0.18",
+ "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz",
+ "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/ajv": {
+ "version": "8.20.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
+ "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-draft-04": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz",
+ "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "ajv": "^8.5.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ajv-i18n": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/ajv-i18n/-/ajv-i18n-4.2.0.tgz",
+ "integrity": "sha512-v/ei2UkCEeuKNXh8RToiFsUclmU+G57LO1Oo22OagNMENIw+Yb8eMwvHu7Vn9fmkjJyv6XclhJ8TbuigSglPkg==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "ajv": "^8.0.0-beta.0"
+ }
+ },
+ "node_modules/am-i-vibing": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/am-i-vibing/-/am-i-vibing-0.4.0.tgz",
+ "integrity": "sha512-MxT4XZL7pzLHpuvhDKdMaQHMGGkJDLluKBLsbstn+8wv9sWcFT6h+0ve9qkml95amVTZtZV83gQe2hY+ojgHLg==",
+ "license": "MIT",
+ "dependencies": {
+ "process-ancestry": "^0.1.0"
+ },
+ "bin": {
+ "am-i-vibing": "dist/cli.mjs"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz",
+ "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "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.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "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/astro": {
+ "version": "7.3.5",
+ "resolved": "https://registry.npmjs.org/astro/-/astro-7.3.5.tgz",
+ "integrity": "sha512-1p/ernaq/RuESbM5NMjWzbO6SiebMGrGcYlGjMfBhzzgM+n/X7AUmVyw8BqW2QYCdivpsHwJBcD1fqmlTZjZHg==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/compiler-rs": "^0.5.0",
+ "@astrojs/internal-helpers": "0.11.0",
+ "@astrojs/markdown-satteri": "0.4.2",
+ "@astrojs/telemetry": "3.3.3",
+ "@capsizecss/unpack": "^4.0.0",
+ "@clack/prompts": "^1.1.0",
+ "@oslojs/encoding": "^1.1.0",
+ "am-i-vibing": "^0.4.0",
+ "aria-query": "^5.3.2",
+ "axobject-query": "^4.1.0",
+ "ci-info": "^4.4.0",
+ "clsx": "^2.1.1",
+ "common-ancestor-path": "^2.0.0",
+ "cookie": "^2.0.1",
+ "devalue": "^5.8.1",
+ "diff": "^9.0.0",
+ "dset": "^3.1.4",
+ "es-module-lexer": "^2.0.0",
+ "esbuild": "^0.28.0",
+ "find-proc": "0.2.0",
+ "flattie": "^1.1.1",
+ "fontace": "~0.4.1",
+ "get-tsconfig": "5.0.0-beta.4",
+ "github-slugger": "^2.0.0",
+ "html-escaper": "3.0.3",
+ "http-cache-semantics": "^4.2.0",
+ "js-yaml": "^4.3.2",
+ "jsonc-parser": "^3.3.1",
+ "magic-string": "^1.0.0",
+ "magicast": "^0.5.2",
+ "mrmime": "^2.0.1",
+ "neotraverse": "^1.0.1",
+ "obug": "^3.0.0",
+ "p-limit": "^7.3.0",
+ "p-queue": "^9.1.0",
+ "package-manager-detector": "^1.6.0",
+ "piccolore": "^0.1.3",
+ "picomatch": "^4.0.4",
+ "shiki": "^4.0.2",
+ "smol-toml": "^1.8.0",
+ "svgo": "^4.1.0",
+ "tinyclip": "^1.0.1",
+ "tinyexec": "^1.0.4",
+ "tinyglobby": "^0.2.15",
+ "ultrahtml": "^1.6.0",
+ "unifont": "~0.7.5",
+ "unstorage": "^1.17.5",
+ "verkit": "^0.4.0",
+ "vite": "^8.0.13",
+ "vitefu": "^1.1.2",
+ "xxhash-wasm": "^1.1.0",
+ "yargs-parser": "^22.0.0",
+ "zod": "^4.5.4"
+ },
+ "bin": {
+ "astro": "bin/astro.mjs"
+ },
+ "engines": {
+ "node": ">=22.12.0",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/astrodotbuild"
+ },
+ "optionalDependencies": {
+ "sharp": "^0.35.4"
+ },
+ "peerDependencies": {
+ "@astrojs/markdown-remark": "^7.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@astrojs/markdown-remark": {
+ "optional": true
+ }
+ }
+ },
+ "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/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/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/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/chokidar": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 14.16.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/ci-info": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz",
+ "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
+ "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^7.2.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/cliui/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==",
+ "dev": true,
+ "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/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/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/commander": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
+ "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/common-ancestor-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-2.0.0.tgz",
+ "integrity": "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-2.0.1.tgz",
+ "integrity": "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=22"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/cookie-es": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz",
+ "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==",
+ "license": "MIT"
+ },
+ "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-select": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-6.0.0.tgz",
+ "integrity": "sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^7.0.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.2.2",
+ "nth-check": "^2.1.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/css-tree": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz",
+ "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==",
+ "license": "MIT",
+ "dependencies": {
+ "mdn-data": "2.27.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-7.0.0.tgz",
+ "integrity": "sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/csso": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
+ "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
+ "license": "MIT",
+ "dependencies": {
+ "css-tree": "~2.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/csso/node_modules/css-tree": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
+ "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
+ "license": "MIT",
+ "dependencies": {
+ "mdn-data": "2.0.28",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/csso/node_modules/mdn-data": {
+ "version": "2.0.28",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
+ "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
+ "license": "CC0-1.0"
+ },
+ "node_modules/defu": {
+ "version": "6.1.7",
+ "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz",
+ "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==",
+ "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.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/devalue": {
+ "version": "5.9.4",
+ "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.4.tgz",
+ "integrity": "sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg==",
+ "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/diff": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz",
+ "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
+ "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "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/emmet": {
+ "version": "2.4.11",
+ "resolved": "https://registry.npmjs.org/emmet/-/emmet-2.4.11.tgz",
+ "integrity": "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==",
+ "dev": true,
+ "license": "MIT",
+ "workspaces": [
+ "./packages/scanner",
+ "./packages/abbreviation",
+ "./packages/css-abbreviation",
+ "./"
+ ],
+ "dependencies": {
+ "@emmetio/abbreviation": "^2.3.3",
+ "@emmetio/css-abbreviation": "^2.1.8"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz",
+ "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==",
+ "license": "MIT"
+ },
+ "node_modules/esbuild": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
+ "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.28.2",
+ "@esbuild/android-arm": "0.28.2",
+ "@esbuild/android-arm64": "0.28.2",
+ "@esbuild/android-x64": "0.28.2",
+ "@esbuild/darwin-arm64": "0.28.2",
+ "@esbuild/darwin-x64": "0.28.2",
+ "@esbuild/freebsd-arm64": "0.28.2",
+ "@esbuild/freebsd-x64": "0.28.2",
+ "@esbuild/linux-arm": "0.28.2",
+ "@esbuild/linux-arm64": "0.28.2",
+ "@esbuild/linux-ia32": "0.28.2",
+ "@esbuild/linux-loong64": "0.28.2",
+ "@esbuild/linux-mips64el": "0.28.2",
+ "@esbuild/linux-ppc64": "0.28.2",
+ "@esbuild/linux-riscv64": "0.28.2",
+ "@esbuild/linux-s390x": "0.28.2",
+ "@esbuild/linux-x64": "0.28.2",
+ "@esbuild/netbsd-arm64": "0.28.2",
+ "@esbuild/netbsd-x64": "0.28.2",
+ "@esbuild/openbsd-arm64": "0.28.2",
+ "@esbuild/openbsd-x64": "0.28.2",
+ "@esbuild/openharmony-arm64": "0.28.2",
+ "@esbuild/sunos-x64": "0.28.2",
+ "@esbuild/win32-arm64": "0.28.2",
+ "@esbuild/win32-ia32": "0.28.2",
+ "@esbuild/win32-x64": "0.28.2"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
+ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
+ "license": "MIT"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-string-truncated-width": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz",
+ "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==",
+ "license": "MIT"
+ },
+ "node_modules/fast-string-width": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz",
+ "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-string-truncated-width": "^3.0.2"
+ }
+ },
+ "node_modules/fast-uri": {
+ "version": "3.1.8",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz",
+ "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/fast-wrap-ansi": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz",
+ "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-string-width": "^3.0.2"
+ }
+ },
+ "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/find-proc": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/find-proc/-/find-proc-0.2.0.tgz",
+ "integrity": "sha512-a6h2nTrgB3g5Wrn4au9Ux4kMk7IJtyF6oA8uGn+mk3weLZssUtc1cp6meHIzzuW2o1srrBAxzkmXLbqjRkgB6w==",
+ "license": "MIT",
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "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.4.1",
+ "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.1.tgz",
+ "integrity": "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==",
+ "license": "MIT",
+ "dependencies": {
+ "fontkitten": "^1.0.2"
+ }
+ },
+ "node_modules/fontkitten": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.3.tgz",
+ "integrity": "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==",
+ "license": "MIT",
+ "dependencies": {
+ "tiny-inflate": "^1.0.3"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "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-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-east-asian-width": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz",
+ "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "5.0.0-beta.4",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-5.0.0-beta.4.tgz",
+ "integrity": "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==",
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=20.20.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "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.11",
+ "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz",
+ "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie-es": "^1.2.3",
+ "crossws": "^0.3.5",
+ "defu": "^6.1.6",
+ "destr": "^2.0.5",
+ "iron-webcrypto": "^1.2.1",
+ "node-mock-http": "^1.0.4",
+ "radix3": "^1.1.2",
+ "ufo": "^1.6.3",
+ "uncrypto": "^0.1.3"
+ }
+ },
+ "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-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/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/http-cache-semantics": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.3.0.tgz",
+ "integrity": "sha512-M5t5LlJpS1UHMjvwRQVdFHvPISGeLAxNcrWuJkeGh0KxsqCHZ1O3NXZU/8x7cD0BDcGW8kapxMKTvwlqrNkHkA==",
+ "license": "BSD-2-Clause"
+ },
+ "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-docker": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-4.0.0.tgz",
+ "integrity": "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==",
+ "license": "MIT",
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "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/js-yaml": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz",
+ "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/jsonc-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
+ "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
+ "license": "MIT"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz",
+ "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==",
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.33.0",
+ "lightningcss-darwin-arm64": "1.33.0",
+ "lightningcss-darwin-x64": "1.33.0",
+ "lightningcss-freebsd-x64": "1.33.0",
+ "lightningcss-linux-arm-gnueabihf": "1.33.0",
+ "lightningcss-linux-arm64-gnu": "1.33.0",
+ "lightningcss-linux-arm64-musl": "1.33.0",
+ "lightningcss-linux-x64-gnu": "1.33.0",
+ "lightningcss-linux-x64-musl": "1.33.0",
+ "lightningcss-win32-arm64-msvc": "1.33.0",
+ "lightningcss-win32-x64-msvc": "1.33.0"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz",
+ "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz",
+ "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz",
+ "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz",
+ "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz",
+ "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz",
+ "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz",
+ "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz",
+ "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz",
+ "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz",
+ "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz",
+ "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "11.5.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz",
+ "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz",
+ "integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.6.0"
+ }
+ },
+ "node_modules/magicast": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.5.tgz",
+ "integrity": "sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "node_modules/mdast-util-to-hast": {
+ "version": "13.2.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz",
+ "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==",
+ "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/mdn-data": {
+ "version": "2.27.1",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz",
+ "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==",
+ "license": "CC0-1.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-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-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-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.3",
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.3.tgz",
+ "integrity": "sha512-oxB2Ik03hI0gv+VNn9tnh1t1YEe9MDPptViAEgfdf3YQHsn0pzGTgCdlSCJXcwhqm8phaEuM7zeEu3QQzVBrPg==",
+ "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/muggle-string": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz",
+ "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.20",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz",
+ "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==",
+ "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": "1.0.1",
+ "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-1.0.1.tgz",
+ "integrity": "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w==",
+ "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-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.5",
+ "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.5.tgz",
+ "integrity": "sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==",
+ "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/obug": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/obug/-/obug-3.0.0.tgz",
+ "integrity": "sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==",
+ "funding": [
+ "https://github.com/sponsors/sxzz",
+ "https://opencollective.com/debug"
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20.0"
+ }
+ },
+ "node_modules/ofetch": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz",
+ "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==",
+ "license": "MIT",
+ "dependencies": {
+ "destr": "^2.0.5",
+ "node-fetch-native": "^1.6.7",
+ "ufo": "^1.6.1"
+ }
+ },
+ "node_modules/ohash": {
+ "version": "2.0.12",
+ "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.12.tgz",
+ "integrity": "sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==",
+ "license": "MIT"
+ },
+ "node_modules/oniguruma-parser": {
+ "version": "0.12.2",
+ "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz",
+ "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==",
+ "license": "MIT"
+ },
+ "node_modules/oniguruma-to-es": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz",
+ "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==",
+ "license": "MIT",
+ "dependencies": {
+ "oniguruma-parser": "^0.12.2",
+ "regex": "^6.1.0",
+ "regex-recursion": "^6.0.2"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.3.tgz",
+ "integrity": "sha512-SKgVvDU5TNIxQ4r30U75BNLC6a+6GOEjngrk/ysM5+Zu3oJuk1SC5ApzIzY/7PjCEjoVPMWJ0zho/4QUiEB7TQ==",
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^1.2.1"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-queue": {
+ "version": "9.3.3",
+ "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.3.tgz",
+ "integrity": "sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA==",
+ "license": "MIT",
+ "dependencies": {
+ "eventemitter3": "^5.0.4",
+ "p-timeout": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-timeout": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.2.tgz",
+ "integrity": "sha512-prbX4Z3YszrFNgH+MW5Zoeq3baXrMtP/MQnFeET90UB/GtGcGDQ5Usg9OCy6ETjTTntOw1SL2z9fMPUppN3Guw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/package-manager-detector": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.9.0.tgz",
+ "integrity": "sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA==",
+ "license": "MIT"
+ },
+ "node_modules/path-browserify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
+ "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/piccolore": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz",
+ "integrity": "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==",
+ "license": "ISC"
+ },
+ "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.7",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
+ "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/playwright": {
+ "version": "1.63.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz",
+ "integrity": "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright-core": "1.63.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.63.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.63.0.tgz",
+ "integrity": "sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.29",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz",
+ "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==",
+ "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.19",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "3.9.7",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.7.tgz",
+ "integrity": "sha512-T3mF5P7HFzZVLDQuCUNtJj6WK1pcpLMweMNUVwGb01bLNkH9AkKxtZvmInw8K6bnn2O3d6/5RHl6zSHiBAD0Og==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-plugin-astro": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-plugin-astro/-/prettier-plugin-astro-1.0.0.tgz",
+ "integrity": "sha512-xOlwKNSaPkPPiJeOhiFk8/MA2eH5A+in99dID+weTJhPcq+FBdIaB/3UNFdy9ckmc8/bT7QNyxSb78cnopgElQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/compiler-rs": "^0.4.0",
+ "@prettier/parse-srcset": "^3.1.0",
+ "sass-formatter": "^0.8.0"
+ },
+ "engines": {
+ "node": ">=22.12.0"
+ },
+ "peerDependencies": {
+ "prettier": "^3.5.3"
+ }
+ },
+ "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding/-/compiler-binding-0.4.1.tgz",
+ "integrity": "sha512-ZYVDW1P58OXyxqZbOMp+/2U9KA7y2esIx9r4pWdpZ/l6fCZhaw4ul+D7EgpRVxYLkyTWqzANbTTUFGPFP8zDHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@astrojs/compiler-binding-android-arm64": "0.4.1",
+ "@astrojs/compiler-binding-darwin-arm64": "0.4.1",
+ "@astrojs/compiler-binding-darwin-x64": "0.4.1",
+ "@astrojs/compiler-binding-linux-arm64-gnu": "0.4.1",
+ "@astrojs/compiler-binding-linux-arm64-musl": "0.4.1",
+ "@astrojs/compiler-binding-linux-x64-gnu": "0.4.1",
+ "@astrojs/compiler-binding-linux-x64-musl": "0.4.1",
+ "@astrojs/compiler-binding-wasm32-wasi": "0.4.1",
+ "@astrojs/compiler-binding-win32-arm64-msvc": "0.4.1",
+ "@astrojs/compiler-binding-win32-x64-msvc": "0.4.1"
+ }
+ },
+ "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-android-arm64": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-android-arm64/-/compiler-binding-android-arm64-0.4.1.tgz",
+ "integrity": "sha512-Wtx2DZORuNTLASLLZ8hlrjFaJe1LUloatcCn9EPq7knBhRpjMGXeJANIJMeBZG4igvz7x+3nyAJM2iwCSW9xog==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-darwin-arm64": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-arm64/-/compiler-binding-darwin-arm64-0.4.1.tgz",
+ "integrity": "sha512-7iwXcU+hB60Y1SvOfnWHNioT72gDsDQKaDipXh2Onwp/TvQMozhbvhy13wJR8Af940a0h6PbddCeJFpj791UDA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-darwin-x64": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-x64/-/compiler-binding-darwin-x64-0.4.1.tgz",
+ "integrity": "sha512-pifjICos49RXhSVKSDu5gDB0/8j0O7DmxSp1DaWW2PyLG5ekwSkcrfOZto5JqhdI7QYMZB3XOiXpsdOXpS4npA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-linux-arm64-gnu": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-gnu/-/compiler-binding-linux-arm64-gnu-0.4.1.tgz",
+ "integrity": "sha512-hsZMlNWc6LqWHh9LlSwTq1/XCY7pxA5rkkKIdZRV8mgbOz9OwLvjEoe5Jry13eIZjMUQO8XnQyzk8o/Pjw2YFA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-linux-arm64-musl": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-musl/-/compiler-binding-linux-arm64-musl-0.4.1.tgz",
+ "integrity": "sha512-a3EW27f+Z9uf57vYmpLgUriA1y9wDhKz57PT8D8XlFZt2HZEWhUxKuxI1jvKVI9p8okYee11WXEm/FHY1G9yoQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-linux-x64-gnu": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-gnu/-/compiler-binding-linux-x64-gnu-0.4.1.tgz",
+ "integrity": "sha512-XruPTKB/SS4DFzeAjQ1ZyM0ieHDjapHkOSnnzRApZ4FXY2YAI7UY3mETjfJiIS/acyZyrnPH5jObv1gATTdUJQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-linux-x64-musl": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-musl/-/compiler-binding-linux-x64-musl-0.4.1.tgz",
+ "integrity": "sha512-E8j7lGanqNpudtkUbsFaOLJhmwFe4Ecqux7K9hkj4wULIPNZRM2bYZNcMSj8r1oWvaUiwZl8LaaNwv3+14jqhw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-wasm32-wasi": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-wasm32-wasi/-/compiler-binding-wasm32-wasi-0.4.1.tgz",
+ "integrity": "sha512-vNLxIv41ZE+nmPFtw24cRW5ZXG+ZPcAGtgigtjCP8lvcxKI7u2GrXoMsCgEH5fMw8WCEJpwLvsVBg15rJKAkyA==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/wasm-runtime": "^1.2.4"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-win32-arm64-msvc": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-arm64-msvc/-/compiler-binding-win32-arm64-msvc-0.4.1.tgz",
+ "integrity": "sha512-/xTDgRsFDoTh0/+KFXJ74DY4Jqn8CoSHwa8UP5RAblHfnRHkenwfSl4zYDET61O9vXKLk09UzHjjq09yP20/AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-win32-x64-msvc": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-x64-msvc/-/compiler-binding-win32-x64-msvc-0.4.1.tgz",
+ "integrity": "sha512-0x5J6iHZO0Fjo/CVzoIbXKzWnGESftDbEUQ/oXQ61HhUAFoR7+T1jfToZSNAS+gYyBXHSTpMWYC7lzarbhClcg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-rs": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-rs/-/compiler-rs-0.4.1.tgz",
+ "integrity": "sha512-//NmAuRhy7eU9iP0ceI5Pymy55M+nUZ//kS6XeOyhBWdDib2KbJOyFaVJ52k7sDkJwTKVox7oPu3SpYiVnGm5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/compiler-binding": "0.4.1"
+ },
+ "engines": {
+ "node": ">=22.12.0"
+ }
+ },
+ "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/process-ancestry": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/process-ancestry/-/process-ancestry-0.1.0.tgz",
+ "integrity": "sha512-tGqJW/UnclpYASFcM6Xh8D8l/BMtaQ9+CSG0vlJSJTcdMM4lDRv4c6H0Pdcsfted+bVczdYSfk2fdukg2gQkZg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/property-information": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz",
+ "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==",
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.18.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/regex": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz",
+ "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==",
+ "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/request-light": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.7.0.tgz",
+ "integrity": "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "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/rolldown": {
+ "version": "1.2.12",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.12.tgz",
+ "integrity": "sha512-8wafseiaG80xmXSfqidUNqZcylTlhmPZZt+za2m+js2sFZ8dTNlhIOV2WcbIPx2hgwPBJpEUGFAMZ9bgBBLTSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/types": "=0.152.0",
+ "@rolldown/pluginutils": "^1.0.0"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm-eabi": "1.2.12",
+ "@rolldown/binding-android-arm64": "1.2.12",
+ "@rolldown/binding-darwin-arm64": "1.2.12",
+ "@rolldown/binding-darwin-x64": "1.2.12",
+ "@rolldown/binding-freebsd-x64": "1.2.12",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.2.12",
+ "@rolldown/binding-linux-arm64-gnu": "1.2.12",
+ "@rolldown/binding-linux-arm64-musl": "1.2.12",
+ "@rolldown/binding-linux-ppc64-gnu": "1.2.12",
+ "@rolldown/binding-linux-s390x-gnu": "1.2.12",
+ "@rolldown/binding-linux-x64-gnu": "1.2.12",
+ "@rolldown/binding-linux-x64-musl": "1.2.12",
+ "@rolldown/binding-openharmony-arm64": "1.2.12",
+ "@rolldown/binding-win32-arm64-msvc": "1.2.12",
+ "@rolldown/binding-win32-x64-msvc": "1.2.12"
+ }
+ },
+ "node_modules/s.color": {
+ "version": "0.0.15",
+ "resolved": "https://registry.npmjs.org/s.color/-/s.color-0.0.15.tgz",
+ "integrity": "sha512-AUNrbEUHeKY8XsYr/DYpl+qk5+aM+DChopnWOPEzn8YKzOhv4l2zH6LzZms3tOZP3wwdOyc0RmTciyi46HLIuA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/sass-formatter": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/sass-formatter/-/sass-formatter-0.8.0.tgz",
+ "integrity": "sha512-Nf4eMGE2O5vnJG7KPCRyksWS9gVKSYoUWtPeMXRRxwZSPVQhSGtL602xVYS65LvMkHmkvKa7xpauF0+/5pPwrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "suf-log": "^2.5.3"
+ },
+ "bin": {
+ "sass-formatter": "dist/cli.js"
+ }
+ },
+ "node_modules/satteri": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/satteri/-/satteri-0.10.5.tgz",
+ "integrity": "sha512-Ao1LKpAEa9Wdg0otgbVKViZHEq9ebdXe4DMrp3s9vQAU0HNIuHnFEuMuOcm0ZIXyV0Yzxj91NvhLpvXZJO/5ZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.5",
+ "@types/hast": "^3.0.5",
+ "@types/mdast": "^4.0.4",
+ "@types/unist": "^3.0.3"
+ },
+ "optionalDependencies": {
+ "@bruits/satteri-darwin-arm64": "0.10.5",
+ "@bruits/satteri-darwin-x64": "0.10.5",
+ "@bruits/satteri-linux-arm64-gnu": "0.10.5",
+ "@bruits/satteri-linux-arm64-musl": "0.10.5",
+ "@bruits/satteri-linux-x64-gnu": "0.10.5",
+ "@bruits/satteri-linux-x64-musl": "0.10.5",
+ "@bruits/satteri-wasm32-wasi": "0.10.5",
+ "@bruits/satteri-win32-arm64-msvc": "0.10.5",
+ "@bruits/satteri-win32-x64-msvc": "0.10.5"
+ }
+ },
+ "node_modules/sax": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz",
+ "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=11.0.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "devOptional": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/sharp": {
+ "version": "0.35.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.5.tgz",
+ "integrity": "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@img/colour": "^1.1.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.8.5"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.35.5",
+ "@img/sharp-darwin-x64": "0.35.5",
+ "@img/sharp-freebsd-wasm32": "0.35.5",
+ "@img/sharp-libvips-darwin-arm64": "1.3.4",
+ "@img/sharp-libvips-darwin-x64": "1.3.4",
+ "@img/sharp-libvips-linux-arm": "1.3.4",
+ "@img/sharp-libvips-linux-arm64": "1.3.4",
+ "@img/sharp-libvips-linux-ppc64": "1.3.4",
+ "@img/sharp-libvips-linux-riscv64": "1.3.4",
+ "@img/sharp-libvips-linux-s390x": "1.3.4",
+ "@img/sharp-libvips-linux-x64": "1.3.4",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.3.4",
+ "@img/sharp-libvips-linuxmusl-x64": "1.3.4",
+ "@img/sharp-linux-arm": "0.35.5",
+ "@img/sharp-linux-arm64": "0.35.5",
+ "@img/sharp-linux-ppc64": "0.35.5",
+ "@img/sharp-linux-riscv64": "0.35.5",
+ "@img/sharp-linux-s390x": "0.35.5",
+ "@img/sharp-linux-x64": "0.35.5",
+ "@img/sharp-linuxmusl-arm64": "0.35.5",
+ "@img/sharp-linuxmusl-x64": "0.35.5",
+ "@img/sharp-webcontainers-wasm32": "0.35.5",
+ "@img/sharp-win32-arm64": "0.35.5",
+ "@img/sharp-win32-ia32": "0.35.5",
+ "@img/sharp-win32-x64": "0.35.5"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/shiki": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.5.0.tgz",
+ "integrity": "sha512-OcRW72ngQvc+MSVWBRIovC/BTW589iUE/Vxav7VeM4RcY9MUjqB+IR6ILG1k9nuW6LiG/nct9uma4/MBtnZbmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/core": "4.5.0",
+ "@shikijs/engine-javascript": "4.5.0",
+ "@shikijs/engine-oniguruma": "4.5.0",
+ "@shikijs/langs": "4.5.0",
+ "@shikijs/themes": "4.5.0",
+ "@shikijs/types": "4.5.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "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/smol-toml": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.9.0.tgz",
+ "integrity": "sha512-hpd+HLON7HdZXqYchMM/+LaTTbdK0AU3NngIJ4KVyWbY9bfQqdL9cD+4yf6dUoU2Ap4VsU0JkQi6FxAI1B2mXQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/cyyynthia"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz",
+ "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==",
+ "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/string-width": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.3.0.tgz",
+ "integrity": "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-east-asian-width": "^1.5.0",
+ "strip-ansi": "^7.1.2"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "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.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/suf-log": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/suf-log/-/suf-log-2.5.3.tgz",
+ "integrity": "sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "s.color": "0.0.15"
+ }
+ },
+ "node_modules/svgo": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.1.0.tgz",
+ "integrity": "sha512-bkxnTg1kSU0guhIBmibA6UUhrQmPVA1XsQLN+ylCd+UWzbnLkySOcXpyk1mrl05f+pcaCx2eHb+sp6BgMZWX+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "commander": "^11.1.0",
+ "css-select": "^6.0.0",
+ "css-tree": "^3.0.1",
+ "css-what": "^7.0.0",
+ "csso": "^5.0.5",
+ "picocolors": "^1.1.1",
+ "sax": "1.6.1"
+ },
+ "bin": {
+ "svgo": "bin/svgo.js"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/svgo"
+ }
+ },
+ "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/tinyclip": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-1.0.3.tgz",
+ "integrity": "sha512-nTv2raKFMzGG6JJp0lpNDsRPdfFE6UHlu2rVEjXo8vkx1ZS3LW2CVXqIaLu8xZlu+6tbFs9V58Qc/Jmk/Ii7wA==",
+ "license": "MIT",
+ "engines": {
+ "node": "^16.14.0 || >= 17.3.0"
+ }
+ },
+ "node_modules/tinyexec": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz",
+ "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "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/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "devOptional": true,
+ "license": "0BSD"
+ },
+ "node_modules/typesafe-path": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/typesafe-path/-/typesafe-path-0.2.2.tgz",
+ "integrity": "sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/typescript": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
+ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-auto-import-cache": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/typescript-auto-import-cache/-/typescript-auto-import-cache-0.3.6.tgz",
+ "integrity": "sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.3.8"
+ }
+ },
+ "node_modules/ufo": {
+ "version": "1.6.4",
+ "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz",
+ "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==",
+ "license": "MIT"
+ },
+ "node_modules/ultrahtml": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.7.0.tgz",
+ "integrity": "sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==",
+ "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": {
+ "version": "8.11.2",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-8.11.2.tgz",
+ "integrity": "sha512-u4UB2/IrKdU6lFxumHmmo1a3fCQO5tzQllRorfoRS63txhrB7xTpSn1PftwC4qEHkOaqP95fCWW4lJzwErwzhQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=22.19.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "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.7.5",
+ "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.5.tgz",
+ "integrity": "sha512-ULe/Cs+ZIsq+dcFofNkhqielCrUJnb5mr+Yc4EBM2VlL+6OZR6+cjtI2mT1bJvRBrVncqHAbLURxmPLcCXzWMg==",
+ "license": "MIT",
+ "dependencies": {
+ "css-tree": "^3.1.0",
+ "ohash": "^2.0.11",
+ "undici": "^8.0.0"
+ }
+ },
+ "node_modules/unist-util-is": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz",
+ "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.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-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.1.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz",
+ "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==",
+ "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-parents": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz",
+ "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==",
+ "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.5",
+ "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz",
+ "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==",
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "^3.1.3",
+ "chokidar": "^5.0.0",
+ "destr": "^2.0.5",
+ "h3": "^1.15.10",
+ "lru-cache": "^11.2.7",
+ "node-fetch-native": "^1.6.7",
+ "ofetch": "^1.5.1",
+ "ufo": "^1.6.3"
+ },
+ "peerDependencies": {
+ "@azure/app-configuration": "^1.8.0",
+ "@azure/cosmos": "^4.2.0",
+ "@azure/data-tables": "^13.3.0",
+ "@azure/identity": "^4.6.0",
+ "@azure/keyvault-secrets": "^4.9.0",
+ "@azure/storage-blob": "^12.26.0",
+ "@capacitor/preferences": "^6 || ^7 || ^8",
+ "@deno/kv": ">=0.9.0",
+ "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0",
+ "@planetscale/database": "^1.19.0",
+ "@upstash/redis": "^1.34.3",
+ "@vercel/blob": ">=0.27.1",
+ "@vercel/functions": "^2.2.12 || ^3.0.0",
+ "@vercel/kv": "^1 || ^2 || ^3",
+ "aws4fetch": "^1.0.20",
+ "db0": ">=0.2.1",
+ "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/unstorage/node_modules/chokidar": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
+ "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 20.19.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/unstorage/node_modules/readdirp": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz",
+ "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20.19.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/verkit": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/verkit/-/verkit-0.4.1.tgz",
+ "integrity": "sha512-mMVzj0TXExtVdlDEq+Mzp0eyOuyznNpFobNM3uAqe3HVm/Ps2c+u17BligMkZjZCA6EiXpoon8iRTer3iu40SQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ },
+ "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-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": "8.3.2",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.2.tgz",
+ "integrity": "sha512-SQr1x6W5vVSbROg7vsyXIaxK9b0G7zsT68acdWWRmnBUsgDieLCRG+Rep9WdZgcposvv/GSnr4GUUBqB3vXq6w==",
+ "license": "MIT",
+ "dependencies": {
+ "lightningcss": "^1.33.0",
+ "picomatch": "^4.0.7",
+ "postcss": "^8.5.28",
+ "rolldown": "~1.2.11",
+ "tinyglobby": "^0.2.17"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.7.1",
+ "esbuild": "^0.27.0 || ^0.28.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "@vitejs/devtools": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "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.3",
+ "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz",
+ "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==",
+ "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 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/volar-service-css": {
+ "version": "0.0.71",
+ "resolved": "https://registry.npmjs.org/volar-service-css/-/volar-service-css-0.0.71.tgz",
+ "integrity": "sha512-wRRFt9BpjMKCazcgOh67MSjUjiWUCAh99DyYSDIOTuxaRjEtDC7PpB0k1Y1wbJIW/pVtMUSVbpPo3UGSm0Byxw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "vscode-css-languageservice": "^6.3.0",
+ "vscode-languageserver-textdocument": "^1.0.11",
+ "vscode-uri": "^3.0.8"
+ },
+ "peerDependencies": {
+ "@volar/language-service": "~2.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@volar/language-service": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/volar-service-emmet": {
+ "version": "0.0.71",
+ "resolved": "https://registry.npmjs.org/volar-service-emmet/-/volar-service-emmet-0.0.71.tgz",
+ "integrity": "sha512-zqjzt6bN95e3CUstBm0PBFAJnrfz0ZAARka87fart46/gNCLLuP3Vujy8V/J8HEziTFLnfkgIASLFYPUhonJcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@emmetio/css-parser": "^0.4.1",
+ "@emmetio/html-matcher": "^1.3.0",
+ "@vscode/emmet-helper": "^2.9.3",
+ "vscode-uri": "^3.0.8"
+ },
+ "peerDependencies": {
+ "@volar/language-service": "~2.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@volar/language-service": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/volar-service-html": {
+ "version": "0.0.71",
+ "resolved": "https://registry.npmjs.org/volar-service-html/-/volar-service-html-0.0.71.tgz",
+ "integrity": "sha512-e8tHPhgQ7ooLfudAEIku+kgd9pWkq3SSz8RbnQDI1+Eb8wbenkLGHqoirLqz5ORLV6wIMr2Iv08RWBG5eOcgpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "vscode-html-languageservice": "^5.3.0",
+ "vscode-languageserver-textdocument": "^1.0.11",
+ "vscode-uri": "^3.0.8"
+ },
+ "peerDependencies": {
+ "@volar/language-service": "~2.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@volar/language-service": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/volar-service-prettier": {
+ "version": "0.0.71",
+ "resolved": "https://registry.npmjs.org/volar-service-prettier/-/volar-service-prettier-0.0.71.tgz",
+ "integrity": "sha512-Rz7JVH3qD108UCdmIEiZvOBNljMt2nLFdbN8AXcDfn7xD9F5I2aCIsDVqBbXw21PsnxG0b7MfwtNF+zPS/NKUg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "vscode-uri": "^3.0.8"
+ },
+ "peerDependencies": {
+ "@volar/language-service": "~2.4.0",
+ "prettier": "^2.2 || ^3.0"
+ },
+ "peerDependenciesMeta": {
+ "@volar/language-service": {
+ "optional": true
+ },
+ "prettier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/volar-service-typescript": {
+ "version": "0.0.71",
+ "resolved": "https://registry.npmjs.org/volar-service-typescript/-/volar-service-typescript-0.0.71.tgz",
+ "integrity": "sha512-yTtM/BVT6hoyEYnDtaCyAtNhdNeS/mhTTABlBOdw3NNiRBUin3IznFJpgfjer4c6RYopiPjjQjc9VFhxVl1mLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-browserify": "^1.0.1",
+ "semver": "^7.6.2",
+ "typescript-auto-import-cache": "^0.3.5",
+ "vscode-languageserver-textdocument": "^1.0.11",
+ "vscode-nls": "^5.2.0",
+ "vscode-uri": "^3.0.8"
+ },
+ "peerDependencies": {
+ "@volar/language-service": "~2.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@volar/language-service": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/volar-service-typescript-twoslash-queries": {
+ "version": "0.0.71",
+ "resolved": "https://registry.npmjs.org/volar-service-typescript-twoslash-queries/-/volar-service-typescript-twoslash-queries-0.0.71.tgz",
+ "integrity": "sha512-9K2k72s4n7rV9s4bX0MyjbX9iBribvKZbBJKuEmTCZfeWJXs6Yh7bGpY4eoc7UufAjvpheBqwyZCOIPBvxCv0A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "vscode-uri": "^3.0.8"
+ },
+ "peerDependencies": {
+ "@volar/language-service": "~2.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@volar/language-service": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/volar-service-yaml": {
+ "version": "0.0.71",
+ "resolved": "https://registry.npmjs.org/volar-service-yaml/-/volar-service-yaml-0.0.71.tgz",
+ "integrity": "sha512-qYGWGuVpUTnZGu5P/CR4KLK4aIR8RrcVnmfZ2eRcj9q/I8VZCoC5yy9FtEvfNvnDp4MU17yhdJcvpQPIqhJS2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "vscode-uri": "^3.0.8",
+ "yaml-language-server": "~1.23.0"
+ },
+ "peerDependencies": {
+ "@volar/language-service": "~2.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@volar/language-service": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vscode-css-languageservice": {
+ "version": "6.3.10",
+ "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.10.tgz",
+ "integrity": "sha512-eq5N9Er3fC4vA9zd9EFhyBG90wtCCuXgRSpAndaOgXMh1Wgep5lBgRIeDgjZBW9pa+332yC9+49cZMW8jcL3MA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vscode/l10n": "^0.0.18",
+ "vscode-languageserver-textdocument": "^1.0.12",
+ "vscode-languageserver-types": "3.17.5",
+ "vscode-uri": "^3.1.0"
+ }
+ },
+ "node_modules/vscode-css-languageservice/node_modules/vscode-languageserver-types": {
+ "version": "3.17.5",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
+ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vscode-html-languageservice": {
+ "version": "5.6.2",
+ "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.6.2.tgz",
+ "integrity": "sha512-ulCrSnFnfQ16YzvwnYUgEbUEl/ZG7u2eV27YhvLObSHKkb8fw1Z9cgsnUwjTEeDIdJDoTDTDpxuhQwoenoLNMg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vscode/l10n": "^0.0.18",
+ "vscode-languageserver-textdocument": "^1.0.12",
+ "vscode-languageserver-types": "^3.17.5",
+ "vscode-uri": "^3.1.0"
+ }
+ },
+ "node_modules/vscode-json-languageservice": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.1.8.tgz",
+ "integrity": "sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "jsonc-parser": "^3.0.0",
+ "vscode-languageserver-textdocument": "^1.0.1",
+ "vscode-languageserver-types": "^3.16.0",
+ "vscode-nls": "^5.0.0",
+ "vscode-uri": "^3.0.2"
+ },
+ "engines": {
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/vscode-jsonrpc": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.3.tgz",
+ "integrity": "sha512-qETYf9PCl1lVfwR+oBmMfc4vP2nqLNA35ABxorw3j06+xlKRgDVxbLgKm4Y/xPL0YWdeDjLoI4ImICIT3nB0zQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/vscode-languageserver": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz",
+ "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "vscode-languageserver-protocol": "3.17.5"
+ },
+ "bin": {
+ "installServerIntoExtension": "bin/installServerIntoExtension"
+ }
+ },
+ "node_modules/vscode-languageserver-protocol": {
+ "version": "3.18.4",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.18.4.tgz",
+ "integrity": "sha512-CCXbyr99dafRJzJH/0Ryk7ly5T8XPmSR0//OuF2w4m+9JiOin7zcAT5iET8GRE7Z9taFBejb3GbM0a0mbjdbrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "vscode-jsonrpc": "9.0.3",
+ "vscode-languageserver-types": "3.18.4"
+ }
+ },
+ "node_modules/vscode-languageserver-textdocument": {
+ "version": "1.0.15",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.15.tgz",
+ "integrity": "sha512-Gsfr9S8LU3S6885cS+NaleasJ0n3V5y/SOTqHCifQiXWFw3G9w9/YItDfwHrlCz+dSZWWZ8ilYVMcJe7Sz7Sxw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vscode-languageserver-types": {
+ "version": "3.18.4",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.18.4.tgz",
+ "integrity": "sha512-nQsacoiijvl0fRf2q28uFDrAVwRhdCa3QveiF7/CX0r18mLR5FU/1WskSsCKOjmEbtteKUnY8x8Yzxop+k0zwg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vscode-languageserver/node_modules/vscode-jsonrpc": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
+ "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/vscode-languageserver/node_modules/vscode-languageserver-protocol": {
+ "version": "3.17.5",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz",
+ "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "vscode-jsonrpc": "8.2.0",
+ "vscode-languageserver-types": "3.17.5"
+ }
+ },
+ "node_modules/vscode-languageserver/node_modules/vscode-languageserver-types": {
+ "version": "3.17.5",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
+ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vscode-nls": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz",
+ "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vscode-uri": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.2.0.tgz",
+ "integrity": "sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "dev": true,
+ "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/wrap-ansi/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==",
+ "dev": true,
+ "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/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/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yaml": {
+ "version": "2.9.1",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz",
+ "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==",
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
+ }
+ },
+ "node_modules/yaml-language-server": {
+ "version": "1.23.0",
+ "resolved": "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-1.23.0.tgz",
+ "integrity": "sha512-3qVyCOexLCWw06PQa5kRPwvMWMZ/eZeCRWUvgD6a0OkqL/4iCnxy2WumbWifa937Uo5xhyWJ0uxlU39ljhNh7A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vscode/l10n": "^0.0.18",
+ "ajv": "^8.17.1",
+ "ajv-draft-04": "^1.0.0",
+ "ajv-i18n": "^4.2.0",
+ "prettier": "^3.8.1",
+ "request-light": "^0.5.7",
+ "vscode-json-languageservice": "4.1.8",
+ "vscode-languageserver": "^9.0.0",
+ "vscode-languageserver-textdocument": "^1.0.1",
+ "vscode-languageserver-types": "^3.16.0",
+ "vscode-uri": "^3.0.2",
+ "yaml": "2.8.3"
+ },
+ "bin": {
+ "yaml-language-server": "bin/yaml-language-server"
+ }
+ },
+ "node_modules/yaml-language-server/node_modules/request-light": {
+ "version": "0.5.8",
+ "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.5.8.tgz",
+ "integrity": "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/yaml-language-server/node_modules/yaml": {
+ "version": "2.8.3",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
+ "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
+ }
+ },
+ "node_modules/yargs": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.2.0.tgz",
+ "integrity": "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^9.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "string-width": "^8.2.1",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^22.0.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "22.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
+ "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
+ "license": "ISC",
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz",
+ "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zod": {
+ "version": "4.6.5",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz",
+ "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "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/website/package.json b/website/package.json
new file mode 100644
index 000000000..645485e6c
--- /dev/null
+++ b/website/package.json
@@ -0,0 +1,32 @@
+{
+ "name": "lolbas-website",
+ "private": true,
+ "version": "1.0.0",
+ "type": "module",
+ "engines": {
+ "node": ">=22.22.0"
+ },
+ "scripts": {
+ "prepare:data": "node scripts/prepare-data.mjs",
+ "dev": "npm run prepare:data && astro dev --host 127.0.0.1",
+ "check": "npm run prepare:data && astro check",
+ "build": "npm run prepare:data && astro build && node scripts/verify-build.mjs",
+ "preview": "astro preview --host 127.0.0.1",
+ "test:data": "node --test tests/data*.test.mjs tests/release.test.mjs",
+ "test": "playwright test",
+ "format": "prettier --write src scripts tests *.mjs *.ts *.json",
+ "format:check": "prettier --check src scripts tests *.mjs *.ts *.json"
+ },
+ "dependencies": {
+ "astro": "7.3.5",
+ "yaml": "2.9.1"
+ },
+ "devDependencies": {
+ "@astrojs/check": "0.9.10",
+ "@playwright/test": "1.63.0",
+ "@types/node": "22.20.3",
+ "prettier": "3.9.7",
+ "prettier-plugin-astro": "1.0.0",
+ "typescript": "6.0.3"
+ }
+}
diff --git a/website/playwright.config.ts b/website/playwright.config.ts
new file mode 100644
index 000000000..7eddac255
--- /dev/null
+++ b/website/playwright.config.ts
@@ -0,0 +1,15 @@
+import { defineConfig, devices } from '@playwright/test';
+export default defineConfig({
+ testDir: './tests',
+ testMatch: process.env.SITE_BASE ? '**/preview.spec.ts' : '**/site.spec.ts',
+ fullyParallel: true,
+ retries: process.env.CI ? 1 : 0,
+ reporter: [['list'], ['html', { open: 'never' }]],
+ use: { baseURL: 'http://127.0.0.1:4388', trace: 'retain-on-failure' },
+ projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
+ webServer: {
+ command: 'npm run preview -- --port 4388 --ignore-lock',
+ url: `http://127.0.0.1:4388${process.env.SITE_BASE || ''}/`,
+ reuseExistingServer: false,
+ },
+});
diff --git a/website/public/THIRD_PARTY_NOTICES.txt b/website/public/THIRD_PARTY_NOTICES.txt
new file mode 100644
index 000000000..5c03b2f20
--- /dev/null
+++ b/website/public/THIRD_PARTY_NOTICES.txt
@@ -0,0 +1,26 @@
+Third-party notices
+
+The GitHub mark comes from GitHub Octicons:
+https://github.com/primer/octicons/blob/main/icons/mark-github-16.svg
+
+MIT License
+
+Copyright (c) 2026 GitHub Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/website/public/favicon.png b/website/public/favicon.png
new file mode 100644
index 000000000..c42cc7a65
Binary files /dev/null and b/website/public/favicon.png differ
diff --git a/website/public/logo.png b/website/public/logo.png
new file mode 100644
index 000000000..a2e8321a6
Binary files /dev/null and b/website/public/logo.png differ
diff --git a/website/scripts/prepare-data.mjs b/website/scripts/prepare-data.mjs
new file mode 100644
index 000000000..cf2b0658c
--- /dev/null
+++ b/website/scripts/prepare-data.mjs
@@ -0,0 +1,322 @@
+import { readFile, readdir, mkdir, writeFile } from 'node:fs/promises';
+import { fileURLToPath } from 'node:url';
+import { dirname, resolve, join } from 'node:path';
+import { parseDocument } from 'yaml';
+
+export const websiteRoot = resolve(
+ dirname(fileURLToPath(import.meta.url)),
+ '..',
+);
+export const sourceRoot = resolve(websiteRoot, '../yml');
+export const canonicalOrigin = 'https://lolbas-project.github.io';
+export const collections = {
+ OSBinaries: 'Binaries',
+ OSLibraries: 'Libraries',
+ OSScripts: 'Scripts',
+ OtherMSBinaries: 'OtherMSBinaries',
+};
+export const apiKeys = [
+ 'Name',
+ 'Description',
+ 'Author',
+ 'Created',
+ 'Commands',
+ 'Full_Path',
+ 'Detection',
+ 'Resources',
+ 'url',
+];
+export const csvHeader = [
+ 'Filename',
+ 'Description',
+ 'Author',
+ 'Date',
+ 'Command',
+ 'Command Description',
+ 'Command Usecase',
+ 'Command Category',
+ 'Command Privileges',
+ 'MITRE ATT&CK technique',
+ 'Operating System',
+ 'Paths',
+ 'Detections',
+ 'Resources',
+ 'Acknowledgements',
+ 'URL',
+ 'Tags',
+];
+const object = (value) =>
+ value !== null && typeof value === 'object' && !Array.isArray(value);
+const fail = (source, message) => {
+ throw new Error(`${source}: ${message}`);
+};
+
+export function parseEntry(text, source, type, id) {
+ const document = parseDocument(text, { uniqueKeys: true, schema: 'core' });
+ if (document.errors.length)
+ fail(source, document.errors.map((error) => error.message).join('; '));
+ const entry = document.toJS({ maxAliasCount: 100 });
+ if (!object(entry)) fail(source, 'expected a YAML mapping');
+ for (const key of ['Name', 'Description', 'Author', 'Created']) {
+ if (typeof entry[key] !== 'string' || !entry[key].trim())
+ fail(source, `${key} must be a non-empty string`);
+ }
+ const createdTime = Date.parse(`${entry.Created}T00:00:00Z`);
+ if (
+ !/^\d{4}-\d{2}-\d{2}$/.test(entry.Created) ||
+ !Number.isFinite(createdTime) ||
+ new Date(createdTime).toISOString().slice(0, 10) !== entry.Created
+ )
+ fail(source, 'Created must be a valid calendar date in YYYY-MM-DD format');
+ if (!Array.isArray(entry.Commands) || !entry.Commands.length)
+ fail(source, 'Commands must be a non-empty array');
+ entry.Commands.forEach((command, i) => {
+ if (!object(command)) fail(source, `Commands[${i}] must be a mapping`);
+ for (const key of [
+ 'Command',
+ 'Description',
+ 'Usecase',
+ 'Category',
+ 'Privileges',
+ 'MitreID',
+ 'OperatingSystem',
+ ]) {
+ if (typeof command[key] !== 'string' || !command[key].trim())
+ fail(source, `Commands[${i}].${key} must be a non-empty string`);
+ }
+ if (!/^T\d{4}(\.\d{3})?$/.test(command.MitreID))
+ fail(source, `Commands[${i}].MitreID must be an ATT&CK technique ID`);
+ if (
+ command.Tags != null &&
+ (!Array.isArray(command.Tags) ||
+ command.Tags.some(
+ (tag) =>
+ !object(tag) ||
+ Object.values(tag).some((value) => typeof value !== 'string'),
+ ))
+ )
+ fail(source, `Commands[${i}].Tags must be an array of string mappings`);
+ });
+ for (const key of [
+ 'Full_Path',
+ 'Detection',
+ 'Resources',
+ 'Acknowledgement',
+ ]) {
+ if (
+ entry[key] != null &&
+ (!Array.isArray(entry[key]) ||
+ entry[key].some(
+ (item) =>
+ !object(item) ||
+ Object.values(item).some(
+ (value) => value != null && typeof value !== 'string',
+ ),
+ ))
+ )
+ fail(source, `${key} must be an array of string mappings`);
+ }
+ if (!/^[A-Za-z0-9_.-]+$/.test(id))
+ fail(source, 'filename must be safe for a legacy URL');
+ return { ...entry, id, type, url: `/lolbas/${type}/${id}/`, source };
+}
+
+export async function loadCatalog(root = sourceRoot) {
+ const records = [];
+ const routes = new Set();
+ const binaryNames = new Map();
+ const folders = new Map();
+ // Match the canonical filename gates, including unpublished collections.
+ // Inspect actual directories only; do not follow symlinks outside the source.
+ const checkExtension = (source) => {
+ if (!source.endsWith('.yml'))
+ fail(source, "unexpected extension; use '.yml' (all lower case)");
+ };
+ const checkTree = async (directory, relative) => {
+ for (const entry of await readdir(directory, { withFileTypes: true })) {
+ const source = `${relative}/${entry.name}`;
+ if (entry.isFile()) checkExtension(source);
+ if (entry.isDirectory())
+ await checkTree(join(directory, entry.name), source);
+ }
+ };
+ await checkTree(root, 'yml');
+ for (const entry of await readdir(root, { withFileTypes: true })) {
+ if (entry.isFile()) checkExtension(`yml/${entry.name}`);
+ if (!entry.isDirectory()) continue;
+ const children = await readdir(join(root, entry.name), {
+ withFileTypes: true,
+ });
+ if (Object.hasOwn(collections, entry.name)) {
+ const nested = children.find((child) => child.isDirectory());
+ if (nested)
+ fail(
+ `yml/${entry.name}/${nested.name}`,
+ 'nested directories are not supported in published collections; keep entries directly in the collection',
+ );
+ }
+ const files = children
+ .filter((file) => file.isFile())
+ .map((file) => file.name)
+ .sort();
+ folders.set(entry.name, files);
+ for (const file of files) {
+ const source = `yml/${entry.name}/${file}`;
+ checkExtension(source);
+ if (entry.name === 'OSBinaries' || entry.name === 'OtherMSBinaries') {
+ const key = file.toLowerCase();
+ if (binaryNames.has(key))
+ fail(
+ source,
+ `duplicate binary filename (case insensitive): ${binaryNames.get(key)}`,
+ );
+ binaryNames.set(key, source);
+ }
+ }
+ }
+ for (const [folder, type] of Object.entries(collections)) {
+ const files = folders.get(folder);
+ if (!files) fail(`yml/${folder}`, 'missing canonical collection directory');
+ for (const file of files) {
+ const record = parseEntry(
+ await readFile(join(root, folder, file), 'utf8'),
+ `yml/${folder}/${file}`,
+ type,
+ file.slice(0, -4),
+ );
+ const key = record.url.toLowerCase();
+ if (routes.has(key)) fail(record.source, 'duplicate legacy route');
+ routes.add(key);
+ records.push(record);
+ }
+ }
+ return records.sort(
+ (a, b) =>
+ a.Name.localeCompare(b.Name, 'en') || a.url.localeCompare(b.url, 'en'),
+ );
+}
+
+export const toApi = (records) =>
+ records.map((record) =>
+ Object.fromEntries(
+ apiKeys.map((key) => [
+ key,
+ key === 'url' ? canonicalOrigin + record.url : (record[key] ?? null),
+ ]),
+ ),
+ );
+const csvCell = (value) => `"${String(value ?? '').replaceAll('"', '""')}"`;
+const joinField = (items, key) =>
+ (items ?? []).map((item) => item[key] ?? '').join(', ');
+export function toCsv(records) {
+ const rows = records.flatMap((record) =>
+ record.Commands.map((command) =>
+ [
+ record.Name,
+ record.Description,
+ record.Author,
+ record.Created,
+ command.Command,
+ command.Description,
+ command.Usecase,
+ command.Category,
+ command.Privileges,
+ command.MitreID,
+ command.OperatingSystem,
+ joinField(record.Full_Path, 'Path'),
+ (record.Detection ?? [])
+ .map((item) => {
+ const [key, value] = Object.entries(item)[0] ?? ['', ''];
+ return `${key}: ${value ?? ''}`;
+ })
+ .join(', '),
+ joinField(record.Resources, 'Link'),
+ (record.Acknowledgement ?? [])
+ .map(
+ (item) =>
+ `${item.Person ?? ''}${item.Handle ? ` (${item.Handle})` : ''}`,
+ )
+ .join(', '),
+ canonicalOrigin + record.url,
+ (command.Tags ?? [])
+ .flatMap((tag) =>
+ Object.entries(tag).map(([key, value]) => `${key}:${value}`),
+ )
+ .join(','),
+ ]
+ .map(csvCell)
+ .join(','),
+ ),
+ );
+ return `${csvHeader.join(',')}\n${rows.join('\n')}\n`;
+}
+
+export function toNavigator(records) {
+ const counts = new Map();
+ for (const record of records)
+ for (const command of record.Commands)
+ counts.set(command.MitreID, (counts.get(command.MitreID) ?? 0) + 1);
+ const parents = new Set(
+ [...counts.keys()]
+ .filter((id) => id.includes('.'))
+ .map((id) => id.split('.')[0]),
+ );
+ return {
+ name: 'The LOLBAS Project',
+ versions: { attack: '19', navigator: '5.3.2', layer: '4.5' },
+ domain: 'enterprise-attack',
+ description:
+ 'ATT&CK alignment of the LOLBAS project, see https://lolbas-project.github.io/.',
+ filters: { platforms: ['Windows'] },
+ layout: {
+ layout: 'flat',
+ aggregateFunction: 'average',
+ showID: false,
+ showName: true,
+ showAggregateScores: false,
+ countUnscored: false,
+ expandedSubtechniques: 'none',
+ },
+ techniques: [...counts]
+ .sort(([a], [b]) => a.localeCompare(b))
+ .map(([techniqueID, score]) => ({
+ techniqueID,
+ score,
+ ...(parents.has(techniqueID) ? { showSubtechniques: true } : {}),
+ })),
+ gradient: {
+ colors: ['#cdf0fc', '#0d89b3'],
+ minValue: 0,
+ maxValue: Math.max(0, ...counts.values()),
+ },
+ selectTechniquesAcrossTactics: true,
+ selectSubtechniquesWithParent: false,
+ selectVisibleTechniques: false,
+ };
+}
+export const toBadge = (count) =>
+ `LOLBAS: ${count} LOLBAS ${count} \n`;
+
+export async function prepareData() {
+ const records = await loadCatalog();
+ const json = (value) => `${JSON.stringify(value, null, 2)}\n`;
+ const outputs = {
+ 'src/data/catalog.json': json(records),
+ 'public/api/lolbas.json': json(toApi(records)),
+ 'public/api/lolbas.csv': toCsv(records),
+ 'public/mitre_attack_navigator_layer.json': json(toNavigator(records)),
+ 'public/assets/lolbas-count.svg': toBadge(records.length),
+ };
+ for (const [path, content] of Object.entries(outputs)) {
+ await mkdir(dirname(join(websiteRoot, path)), { recursive: true });
+ await writeFile(join(websiteRoot, path), content);
+ }
+ console.log(
+ `Prepared ${records.length} LOLBAS entries and ${records.reduce((n, entry) => n + entry.Commands.length, 0)} commands from local YAML.`,
+ );
+}
+if (
+ process.argv[1] &&
+ resolve(process.argv[1]) === fileURLToPath(import.meta.url)
+)
+ await prepareData();
diff --git a/website/scripts/verify-build.mjs b/website/scripts/verify-build.mjs
new file mode 100644
index 000000000..452e587d4
--- /dev/null
+++ b/website/scripts/verify-build.mjs
@@ -0,0 +1,47 @@
+import assert from 'node:assert/strict';
+import { readFile, access } from 'node:fs/promises';
+import { join, resolve } from 'node:path';
+import {
+ loadCatalog,
+ toApi,
+ toCsv,
+ toNavigator,
+ toBadge,
+ websiteRoot,
+} from './prepare-data.mjs';
+
+const dist = resolve(websiteRoot, process.argv[2] ?? 'dist');
+const catalog = await loadCatalog();
+const json = async (path) =>
+ JSON.parse(await readFile(join(dist, path), 'utf8'));
+assert.deepEqual(
+ await json('api/lolbas.json'),
+ toApi(catalog),
+ 'JSON API must preserve legacy fields and all canonical entries',
+);
+assert.equal(
+ await readFile(join(dist, 'api/lolbas.csv'), 'utf8'),
+ toCsv(catalog),
+ 'CSV must include each command with the legacy 17-column representation',
+);
+assert.deepEqual(
+ await json('mitre_attack_navigator_layer.json'),
+ toNavigator(catalog),
+ 'Navigator must aggregate all command techniques',
+);
+assert.equal(
+ await readFile(join(dist, 'assets/lolbas-count.svg'), 'utf8'),
+ toBadge(catalog.length),
+);
+await access(join(dist, 'index.html'));
+for (const entry of catalog) {
+ const html = await readFile(join(dist, entry.url, 'index.html'), 'utf8');
+ assert.ok(html.includes(entry.Name), `${entry.url} must show ${entry.Name}`);
+ assert.ok(
+ html.includes(entry.source),
+ `${entry.url} must link its canonical source`,
+ );
+}
+console.log(
+ `Verified ${catalog.length} legacy detail routes, JSON API, CSV command rows, Navigator and count badge in ${dist}.`,
+);
diff --git a/website/src/components/Explorer.astro b/website/src/components/Explorer.astro
new file mode 100644
index 000000000..1b284316d
--- /dev/null
+++ b/website/src/components/Explorer.astro
@@ -0,0 +1,344 @@
+---
+import { sitePath } from '../lib/site';
+import '../styles/filters.css';
+import { catalog, typeLabels, categories, techniques } from '../lib/catalog';
+const sorted = [...catalog].sort((a, b) => a.Name.localeCompare(b.Name));
+---
+
+
+
+
+
Catalog
+
Trusted Windows tools. Unexpected possibilities.
+
+
+ Use the API ↗
+
+
+
+
+
+
+ {catalog.length} entries to explore
+
+
+ Try{' '}
+
+ /download
+ {' '}
+ or{' '}
+
+ #Script
+
+
+
+
+
+
+
No entries found.
+
Try a different search or reset your filters.
+
+ Clear all filters
+
+
+
+
+
+ All entries are shown. Enable JavaScript for search and filters.
+
+
+
+
+
diff --git a/website/src/components/GitHubIcon.astro b/website/src/components/GitHubIcon.astro
new file mode 100644
index 000000000..5ae4e3244
--- /dev/null
+++ b/website/src/components/GitHubIcon.astro
@@ -0,0 +1,11 @@
+
+
+
diff --git a/website/src/components/Growth.astro b/website/src/components/Growth.astro
new file mode 100644
index 000000000..52df71d2a
--- /dev/null
+++ b/website/src/components/Growth.astro
@@ -0,0 +1,121 @@
+---
+import { sitePath } from '../lib/site';
+import { catalog } from '../lib/catalog';
+const years = [...new Set(catalog.map((e) => e.Created.slice(0, 4)))].sort();
+let total = 0;
+const points = years.map((year) => {
+ const added = catalog.filter((e) => e.Created.startsWith(year)).length;
+ total += added;
+ return { year, added, total };
+});
+const coords = points.map(
+ (p, i) =>
+ `${35 + (i * 550) / Math.max(points.length - 1, 1)},${112 - (p.total / catalog.length) * 90}`,
+);
+const path = 'M' + coords.join(' L');
+---
+
+
+
+
+ THE KNOWLEDGE KEEPS GROWING
+
A growing body of research.
+
+
+ ↗
+
+
+
+ {catalog.length}
+
+ documented entries
+
+ since {years[0]}
+
+
+ Cumulative entries
+
+
+
+
+
+
+
+
+
+ {[22, 67, 112].map((y) => (
+
+ ))}
+
+
+ {points.map((p, i) => (
+
+
+
+ {p.year}: {p.total} total, {p.added} added
+
+
+ {p.year}
+
+
+ ))}
+
+
+ Based on entry creation dates. Catalog growth, not threat activity.
+
+
+ View chart data
+
+
+
+ Year
+ Added
+ Total
+
+
+
+ {points.map((p) => (
+
+
+ {p.year}
+
+ {p.added}
+ {p.total}
+
+ ))}
+
+
+
+
diff --git a/website/src/layouts/Base.astro b/website/src/layouts/Base.astro
new file mode 100644
index 000000000..5b6305e8a
--- /dev/null
+++ b/website/src/layouts/Base.astro
@@ -0,0 +1,107 @@
+---
+import { sitePath } from '../lib/site';
+import GitHubIcon from '../components/GitHubIcon.astro';
+import '../styles/global.css';
+interface Props {
+ title?: string;
+ description?: string;
+}
+const {
+ title = 'LOLBAS · Living Off The Land Binaries, Scripts and Libraries',
+ description = 'Explore Windows binaries, scripts and libraries used in living off the land techniques. Search commands, ATT&CK mappings and detection resources.',
+} = Astro.props;
+---
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+ Skip to content
+
+
+
+
+
+
+
+
+
diff --git a/website/src/lib/catalog.ts b/website/src/lib/catalog.ts
new file mode 100644
index 000000000..ff67acb07
--- /dev/null
+++ b/website/src/lib/catalog.ts
@@ -0,0 +1,51 @@
+import data from '../data/catalog.json';
+
+export interface Command {
+ Command: string;
+ Description: string;
+ Usecase: string;
+ Category: string;
+ Privileges: string;
+ MitreID: string;
+ OperatingSystem: string;
+ Tags?: Record[];
+}
+export type CatalogType =
+ 'Binaries' | 'Libraries' | 'Scripts' | 'OtherMSBinaries';
+export interface CatalogEntry {
+ id: string;
+ type: CatalogType;
+ url: string;
+ source: string;
+ Name: string;
+ Description: string;
+ Aliases?: { Alias: string }[] | null;
+ Author: string;
+ Created: string;
+ Commands: Command[];
+ Full_Path?: { Path: string }[] | null;
+ Detection?: Record[] | null;
+ Resources?: { Link: string }[] | null;
+ Acknowledgement?: { Person: string; Handle?: string | null }[] | null;
+}
+export const catalog = data as CatalogEntry[];
+export const typeLabels: Record = {
+ Binaries: 'OS Binaries',
+ Libraries: 'OS Libraries',
+ Scripts: 'OS Scripts',
+ OtherMSBinaries: 'Other Microsoft Binaries',
+};
+export const categories = [
+ ...new Set(
+ catalog.flatMap((entry) =>
+ entry.Commands.map((command) => command.Category),
+ ),
+ ),
+].sort();
+export const techniques = [
+ ...new Set(
+ catalog.flatMap((entry) =>
+ entry.Commands.map((command) => command.MitreID),
+ ),
+ ),
+].sort();
diff --git a/website/src/lib/site.ts b/website/src/lib/site.ts
new file mode 100644
index 000000000..6dd035fa1
--- /dev/null
+++ b/website/src/lib/site.ts
@@ -0,0 +1,3 @@
+// UI links support a fork's project Pages path; exported API URLs stay canonical.
+export const sitePath = (path: string) =>
+ `${import.meta.env.BASE_URL.replace(/\/$/, '')}${path}`;
diff --git a/website/src/pages/404.astro b/website/src/pages/404.astro
new file mode 100644
index 000000000..44fafd15f
--- /dev/null
+++ b/website/src/pages/404.astro
@@ -0,0 +1,20 @@
+---
+import { sitePath } from '../lib/site';
+import Base from '../layouts/Base.astro';
+---
+
+
+
+ 404 / NO LEAD HERE
+
+ Back to the hunt.
+
+
+ This page doesn’t exist. Search the catalog to find a binary, script, or
+ library.
+
+
+ Explore the catalog →
+
+
+
diff --git a/website/src/pages/about.astro b/website/src/pages/about.astro
new file mode 100644
index 000000000..9c921112d
--- /dev/null
+++ b/website/src/pages/about.astro
@@ -0,0 +1,106 @@
+---
+import Base from '../layouts/Base.astro';
+// Maintainer names and handles are published in the project's README.
+const maintainers = [
+ { name: 'Oddvar Moe', handle: 'oddvarmoe' },
+ { name: 'Jimmy Bayne', handle: 'bohops' },
+ { name: 'Conor Richard', handle: 'xenosCR' },
+ { name: 'Chris “Lopi” Spehn', handle: 'ConsciousHacker' },
+ { name: 'Liam', handle: 'liamsomerville' },
+ { name: 'Wietze', handle: 'Wietze' },
+ { name: 'Jose Hernandez', handle: '_josehelps' },
+];
+---
+
+
+
+
+ Nothing to LOL about.
+
+
+ Living Off The Land Binaries, Scripts and Libraries. A community effort to
+ document the unexpected capabilities of trusted Windows tools.
+
+
+
+
+ Project maintainers
+ The people who keep LOLBAS moving. Find them on X.
+
+ {maintainers.map(({ name, handle }) => (
+
+
+ {name}
+ @{handle} ↗
+
+
+ ))}
+
+
+ The goal
+
+ Document every Microsoft-signed binary, script, and library that can be
+ used for living off the land techniques. Legitimate tools can execute
+ code, transfer files, bypass application allowlisting, access credentials,
+ and perform other actions beyond their expected use.
+
+ What belongs in LOLBAS?
+
+
+ A Microsoft-signed file, native to Windows or available from Microsoft.
+
+
+ Unexpected functionality, with an exception for application allowlisting
+ bypasses.
+
+
+ Functionality useful in adversary simulation or to an advanced threat
+ actor.
+
+
+
+ Read the{' '}
+
+ full criteria
+ {' '}
+ before submitting a finding.
+
+ Built by the community
+
+ The phrase “living off the land” was coined by Christopher Campbell and
+ Matt Graeber at DerbyCon 3. LOLBAS grew from community research into a
+ shared catalog maintained by volunteers.
+
+ The original project logos were created by Adam Nadrowski.
+ Add your finding
+
+ Start with the{' '}
+
+ contribution guide
+ {' '}
+ and{' '}
+
+ YAML template
+
+ . The original source format remains unchanged.
+
+
+ Explore the repository ↗
+
+ Keep exploring
+
+ For Unix binaries, visit{' '}
+ GTFOBins . For Windows drivers,
+ visit LOLDrivers .
+
+
+
+ License & attribution
+ {' '}
+ ·{' '}
+
+ Report an issue
+
+
+
+
diff --git a/website/src/pages/api.astro b/website/src/pages/api.astro
new file mode 100644
index 000000000..e6e8231f2
--- /dev/null
+++ b/website/src/pages/api.astro
@@ -0,0 +1,109 @@
+---
+import { sitePath } from '../lib/site';
+import Base from '../layouts/Base.astro';
+import { catalog } from '../lib/catalog';
+---
+
+
+
+ OPEN DATA. NO KEYS REQUIRED.
+
+ Built for your workflow.
+
+
+ The same LOLBAS data, ready for your tools. Access all {catalog.length}{' '}
+ entries as JSON, CSV, or the original YAML.
+
+
+
+
+ 01 / JSON
+ Every entry. One file.
+
+ The JSON API keeps the existing URL and field structure. Each record
+ includes its name, description, author, creation date, commands, full
+ paths, detections, resources, and canonical page URL. Optional top-level
+ fields retain null values when absent.
+
+
+ /api/lolbas.json ↗
+
+
+
curl https://lolbas-project.github.io/api/lolbas.json
+
+ Copy
+
+
+
+
+ 02 / CSV
+ Ready for a spreadsheet.
+
+ One row per command example, with the existing 17 columns: filename,
+ entry metadata, command metadata, ATT&CK technique, operating
+ systems, paths, detections, resources, acknowledgements, URL, and tags.
+
+
+ /api/lolbas.csv ↗
+
+
+
curl -o lolbas.csv https://lolbas-project.github.io/api/lolbas.csv
+
+ Copy
+
+
+
+
+ 03 / SOURCE YAML
+ Straight from the source.
+
+ YAML files in the project repository remain the source of truth. Browse
+ the four published collections, review changes, and contribute through
+ GitHub.
+
+
+ Browse source files ↗
+
+
+
+ Example: Certutil.yml ↗
+
+
+
+ ATT&CK Navigator
+
+ Download the{' '}
+
+ Navigator layer
+ {' '}
+ to explore command mappings. Scores represent the number of command
+ examples mapped to each technique.
+
+
+ Data is generated at build time from the repository. No API key, cookies,
+ or account needed. See the project{' '}
+
+ licensing notice
+ {' '}
+ for attribution and reuse.
+
+
+
+
diff --git a/website/src/pages/functions.astro b/website/src/pages/functions.astro
new file mode 100644
index 000000000..07ee92c02
--- /dev/null
+++ b/website/src/pages/functions.astro
@@ -0,0 +1,26 @@
+---
+import { sitePath } from '../lib/site';
+import Base from '../layouts/Base.astro';
+import { categories } from '../lib/catalog';
+---
+
+
+
+
+ Unexpected capabilities.
+
+ Explore the functions documented in the catalog.
+
+
+ {categories.map((c) => (
+
+ ))}
+
+
diff --git a/website/src/pages/index.astro b/website/src/pages/index.astro
new file mode 100644
index 000000000..885ebc965
--- /dev/null
+++ b/website/src/pages/index.astro
@@ -0,0 +1,139 @@
+---
+import { sitePath } from '../lib/site';
+import Base from '../layouts/Base.astro';
+import Growth from '../components/Growth.astro';
+import Explorer from '../components/Explorer.astro';
+import { catalog, techniques, categories } from '../lib/catalog';
+const commands = catalog.reduce((sum, e) => sum + e.Commands.length, 0);
+const recent = [...catalog]
+ .sort((a, b) => b.Created.localeCompare(a.Created))
+ .slice(0, 3);
+---
+
+
+
+
+
+ Living Off The Land
+
+ Binaries, Scripts and Libraries
+
+
+ The tools are legitimate. The intent isn’t always.
+
+ Explore the Windows binaries, scripts, and libraries that turn
+ everyday functionality into unexpected
+ capabilities.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LOLBAS / RESEARCH
+
+
+ PS C:\> find the unexpected
+ _
+
+
+
+
+
+ LEGITIMATE BINARY. UNEXPECTED BEHAVIOR.
+
+
+
01 / OBSERVE
+
02 / UNDERSTAND
+
+
+
+
+
+ DOCUMENTED ENTRIES
+
+ {catalog.length}
+ binaries, scripts & libraries
+
+
+
+ COMMAND EXAMPLES
+
+ {commands}
+ unexpected capabilities
+
+
+
+ ATT&CK TECHNIQUES
+
+ {techniques.length}
+ mapped to the framework
+
+
+
+
+
+
+
diff --git a/website/src/pages/insights.astro b/website/src/pages/insights.astro
new file mode 100644
index 000000000..be8c04465
--- /dev/null
+++ b/website/src/pages/insights.astro
@@ -0,0 +1,128 @@
+---
+import { sitePath } from '../lib/site';
+import Base from '../layouts/Base.astro';
+import Growth from '../components/Growth.astro';
+import { catalog, categories, typeLabels, techniques } from '../lib/catalog';
+const distribution = categories
+ .map((category) => ({
+ category,
+ count: catalog.filter((e) =>
+ e.Commands.some((c) => c.Category === category),
+ ).length,
+ }))
+ .sort((a, b) => b.count - a.count);
+const coverage = catalog.filter((e) =>
+ e.Detection?.some((d) => Object.keys(d).some((k) => k !== 'IOC')),
+).length;
+---
+
+
+
+ FOLLOW THE RESEARCH
+
+ The bigger picture.
+
+
+ How the catalog grows. What the tools can do. Where detection resources
+ exist.
+ A view of the community’s research, grounded in the source data.
+
+
+
+
+
+ WHAT’S IN THE CATALOG
+ By file type.
+ {Object.entries(typeLabels).map(([type, label]) => {
+ const count = catalog.filter((e) => e.type === type).length;
+ return (
+
+ {label}
+
+
+
+ {count}
+
+ );
+ })}
+
+ {catalog.length} entries from the four published YAML collections.
+ Honorable mentions remain outside the published catalog.
+
+
+
+ UNEXPECTED CAPABILITIES
+ Functions across the catalog.
+ {distribution.map(({ category, count }) => (
+
+ {category}
+
+
+
+ {count}
+
+ ))}
+
+ Count of entries with at least one command in each category. An entry
+ can have multiple functions, so these counts overlap.
+
+
+
+ TURN RESEARCH INTO DEFENSE
+ Detection resources.
+
+ {coverage}
+ / {catalog.length}
+
+
+ Entries with at least one published detection link, such as Sigma,
+ Elastic, Splunk, analysis, or block rules. IOC-only entries are
+ excluded. Links are research leads; coverage does not guarantee that a
+ technique will be detected.
+
+
+ Explore detection resources ↗
+
+
+ {techniques.length} ATT&CK technique IDs.
+
+ Distinct technique and sub-technique IDs mapped to command examples.
+
+
+ Download Navigator layer ↓
+
+
+
+
+
diff --git a/website/src/pages/lolbas/[...entry].astro b/website/src/pages/lolbas/[...entry].astro
new file mode 100644
index 000000000..7a71db9eb
--- /dev/null
+++ b/website/src/pages/lolbas/[...entry].astro
@@ -0,0 +1,437 @@
+---
+import { sitePath } from '../../lib/site';
+import Base from '../../layouts/Base.astro';
+import { catalog, typeLabels, type CatalogEntry } from '../../lib/catalog';
+import '../../styles/detail.css';
+
+export function getStaticPaths() {
+ return catalog.map((entry) => ({
+ params: { entry: `${entry.type}/${entry.id}` },
+ props: { entry },
+ }));
+}
+interface ExtendedEntry extends CatalogEntry {
+ Aliases?: { Alias: string }[] | null;
+ Code_Sample?: { Code: string }[] | null;
+}
+const { entry } = Astro.props as { entry: ExtendedEntry };
+const groups = [...new Set(entry.Commands.map((command) => command.Category))];
+const techniques = [
+ ...new Set(entry.Commands.map((command) => command.MitreID).filter(Boolean)),
+];
+const detections = (entry.Detection ?? [])
+ .flatMap((item) => Object.entries(item))
+ .filter((item): item is [string, string] => Boolean(item[1]));
+const detectionLinks = detections.filter(
+ ([kind, value]) => kind !== 'IOC' && /^https?:\/\//i.test(value),
+);
+const indicators = detections.filter(
+ ([kind, value]) => kind === 'IOC' || !/^https?:\/\//i.test(value),
+);
+const isLink = (value: string) => /^https?:\/\//i.test(value);
+const attackURL = (id: string) =>
+ `https://attack.mitre.org/techniques/${id.replaceAll('.', '/')}/`;
+function linkLabel(value: string) {
+ try {
+ const url = new URL(value);
+ const pieces = url.pathname.split('/').filter(Boolean);
+ const last = decodeURIComponent(pieces.at(-1) ?? '');
+ if (/^(twitter\.com|x\.com)$/.test(url.hostname))
+ return `Research by @${pieces[0]}`;
+ if (url.hostname === 'gist.github.com')
+ return `Code & research by ${pieces[0]}`;
+ if (!last || /^[a-f0-9]{16,}$/i.test(last)) return url.hostname;
+ return last.replace(/\.html?$/, '').replace(/-/g, ' ');
+ } catch {
+ return value;
+ }
+}
+function host(value: string) {
+ try {
+ return new URL(value).hostname;
+ } catch {
+ return '';
+ }
+}
+const sourceURL = `https://github.com/LOLBAS-Project/LOLBAS/blob/master/${entry.source}`;
+const editURL = `https://github.com/LOLBAS-Project/LOLBAS/edit/master/${entry.source}`;
+const related = catalog
+ .filter(
+ (other) =>
+ other.id !== entry.id &&
+ other.Commands.some((command) => techniques.includes(command.MitreID)),
+ )
+ .slice(0, 3);
+---
+
+
+
+
+ Catalog
+ /
+ {typeLabels[entry.type]}
+ /
+ {entry.Name}
+
+
+
+
+ >_
+
+
+
+ LIVING OFF THE LAND /{' '}
+ {typeLabels[entry.type].toUpperCase()}
+
+
{entry.Name}
+
+
+
+ View source ↗
+
+
+ {entry.Description}
+
+ {groups.map((category) => (
+
+ {category}
+
+ ))}
+ {entry.Aliases?.map((alias) => (
+
Alias: {alias.Alias}
+ ))}
+
+
+
+ {String(entry.Commands.length).padStart(2, '0')}
+ COMMAND EXAMPLES
+
+
+ {String(techniques.length).padStart(2, '0')}
+ ATT&CK TECHNIQUES
+
+
+ {String(detectionLinks.length).padStart(2, '0')}
+ DETECTION RESOURCES
+
+
+ {String(groups.length).padStart(2, '0')}
+ CAPABILITIES
+
+
+
+
+
+
+
+
+ 01 / TRADECRAFT
+
Command reference
+
+
{entry.Commands.length} examples
+
+ {groups.map((category) => (
+
+
+
+ {category}
+
+ {
+ entry.Commands.filter(
+ (command) => command.Category === category,
+ ).length
+ }
+
+
+ {entry.Commands.filter(
+ (command) => command.Category === category,
+ ).map((command, index) => (
+
+
+ {command.Description}
+
+
+ >_ COMMAND
+
+
{command.Command}
+
+ Copy
+
+
+
+
+
USE CASE
+ {command.Usecase}
+
+
+
PRIVILEGES
+ {command.Privileges || 'Not specified'}
+
+
+
OPERATING SYSTEM
+ {command.OperatingSystem || 'Not specified'}
+
+
+ {(command.Tags?.length ?? 0) > 0 && (
+
+ {command.Tags?.flatMap((tag) => Object.entries(tag)).map(
+ ([key, value]) => (
+
+ {key} · {value}
+
+ ),
+ )}
+
+ )}
+
+ ))}
+
+ ))}
+
+
+
+
+ 02 / ON DISK
+
Known paths
+
+
+
+ {entry.Full_Path?.length ? (
+ entry.Full_Path.map((path) => (
+
+ ↳
+ {path.Path}
+
+ ))
+ ) : (
+
No paths documented for this entry.
+ )}
+
+
+
+
+
+ 03 / DEFENSIVE INTELLIGENCE
+
Detection & indicators
+
+
{detectionLinks.length} resources
+
+ {detectionLinks.length > 0 && (
+
+ )}
+ {indicators.length > 0 && (
+
+
+ Indicators & detection notes
+
+
+
+ )}
+ {detections.length === 0 && (
+
+
No detection resources documented yet.
+
+ Contribute detection coverage ↗
+
+
+ )}
+
+ {(entry.Code_Sample?.length ?? 0) > 0 && (
+
+
+
+ REFERENCE IMPLEMENTATIONS
+
Code samples
+
+
+
+ {entry.Code_Sample?.map((sample) => (
+
+ {isLink(sample.Code) ? (
+
+ CODE
+
+ {linkLabel(sample.Code)}
+ {host(sample.Code)}
+
+
+ ↗
+
+
+ ) : (
+ {sample.Code}
+ )}
+
+ ))}
+
+
+ )}
+ {(entry.Resources?.length ?? 0) > 0 && (
+
+
+
+ FURTHER READING
+
Research & resources
+
+
+
+
+ )}
+ {(entry.Acknowledgement?.length ?? 0) > 0 && (
+
+
+
+ COMMUNITY RESEARCH
+
Acknowledgements
+
+
+
+ {entry.Acknowledgement?.map((person) => (
+
+
+ {person.Person?.charAt(0).toUpperCase()}
+
+
+ {person.Person}
+ {person.Handle && {person.Handle} }
+
+
+ ))}
+
+
+ )}
+
+
+
+ ON THIS PAGE
+
+ Command reference {entry.Commands.length}
+
+ {groups.map((category) => (
+
+ {category}
+
+ ))}
+ Known paths
+ Detection & indicators
+ {(entry.Code_Sample?.length ?? 0) > 0 && (
+ Code samples
+ )}
+ {(entry.Resources?.length ?? 0) > 0 && (
+ Research & resources
+ )}
+ {(entry.Acknowledgement?.length ?? 0) > 0 && (
+ Acknowledgements
+ )}
+
+
+ ENTRY DETAILS
+
+
+
TYPE
+ {typeLabels[entry.type]}
+
+
+
AUTHOR
+ {entry.Author}
+
+
+
ADDED TO CATALOG
+
+ {entry.Created}
+
+
+
+
+
+ Improve this entry ↗
+
+
+ {related.length > 0 && (
+
+ SHARED ATT&CK TECHNIQUES
+ Keep exploring
+ {related.map((other) => (
+
+ {other.Name}
+ →
+
+ ))}
+
+ )}
+
+
+
diff --git a/website/src/scripts/explorer.ts b/website/src/scripts/explorer.ts
new file mode 100644
index 000000000..e5b86ca6c
--- /dev/null
+++ b/website/src/scripts/explorer.ts
@@ -0,0 +1,228 @@
+// LOL-SITE-002: filters apply to the same record; URL state is reloadable.
+const form = document.querySelector('#catalog-form');
+if (form) {
+ const controls = [
+ 'q',
+ 'type',
+ 'category',
+ 'technique',
+ 'detection',
+ 'sort',
+ 'year',
+ ];
+ const field = (name: string) =>
+ form.elements.namedItem(name) as HTMLInputElement | HTMLSelectElement;
+ const rows = Array.from(
+ document.querySelectorAll('[data-row]'),
+ );
+ const count = document.querySelector('#result-count')!;
+ const body = document.querySelector('#catalog-rows')!;
+ const previous = document.querySelector('#previous')!;
+ const next = document.querySelector('#next')!;
+ const size = 25;
+ let page = 1;
+ function load() {
+ const params = new URLSearchParams(location.search);
+ controls.forEach((name) => {
+ field(name).value = params.get(name) || (name === 'sort' ? 'name' : '');
+ });
+ page = Math.max(1, Number.parseInt(params.get('page') || '1') || 1);
+ }
+ function update(sync = true) {
+ const q = field('q').value.trim().toLowerCase();
+ const tokens =
+ q.match(/\/(?:awl bypass|uac bypass|[a-z]+)|#[\w]+|[^\s]+/g) || [];
+ const filtered = rows.filter((row) => {
+ const d = row.dataset;
+ const cats = JSON.parse(d.categories || '[]') as string[];
+ const techs = JSON.parse(d.techniques || '[]') as string[];
+ return (
+ (!field('type').value || field('type').value === d.type) &&
+ (!field('category').value || cats.includes(field('category').value)) &&
+ (!field('technique').value ||
+ techs.includes(field('technique').value)) &&
+ (!field('detection').value ||
+ field('detection').value === d.detection) &&
+ (!field('year').value || d.created?.startsWith(field('year').value)) &&
+ tokens.every((token) =>
+ token.startsWith('/')
+ ? cats.some((c) => c.toLowerCase() === token.slice(1).trim())
+ : token.startsWith('#')
+ ? (d.type || '')
+ .toLowerCase()
+ .includes(token.slice(1).replace(/y$/, 'ie'))
+ : d.search?.includes(token),
+ )
+ );
+ });
+ const sort = field('sort').value;
+ const sortSpecs: Record<
+ string,
+ { key: string; descending: boolean; numeric?: boolean }
+ > = {
+ name: { key: 'name', descending: false },
+ reverse: { key: 'name', descending: true },
+ newest: { key: 'created', descending: true },
+ oldest: { key: 'created', descending: false },
+ commands: { key: 'commands', descending: true, numeric: true },
+ detections: { key: 'detections', descending: true, numeric: true },
+ 'detections-asc': { key: 'detections', descending: false, numeric: true },
+ functions: { key: 'functions', descending: false },
+ 'functions-desc': { key: 'functions', descending: true },
+ type: { key: 'typeLabel', descending: false },
+ 'type-desc': { key: 'typeLabel', descending: true },
+ attack: { key: 'attack', descending: false },
+ 'attack-desc': { key: 'attack', descending: true },
+ };
+ const spec = sortSpecs[sort] || sortSpecs.name;
+ filtered.sort((a, b) => {
+ const left = a.dataset[spec.key] || '',
+ right = b.dataset[spec.key] || '';
+ const primary = spec.numeric
+ ? Number(left) - Number(right)
+ : left.localeCompare(right, 'en', { numeric: spec.key === 'attack' });
+ return (
+ primary * (spec.descending ? -1 : 1) ||
+ (a.dataset.name || '').localeCompare(b.dataset.name || '', 'en')
+ );
+ });
+ document
+ .querySelectorAll('[data-sort]')
+ .forEach((button) => {
+ const key = button.dataset.sort!;
+ const active =
+ key === 'name'
+ ? spec.key === 'name'
+ : key === 'type'
+ ? spec.key === 'typeLabel'
+ : spec.key === key;
+ button
+ .closest('th')!
+ .setAttribute(
+ 'aria-sort',
+ active ? (spec.descending ? 'descending' : 'ascending') : 'none',
+ );
+ button.querySelector('.sort-indicator')!.textContent = active
+ ? spec.descending
+ ? '↓'
+ : '↑'
+ : '↕';
+ });
+ const pages = Math.max(1, Math.ceil(filtered.length / size));
+ page = Math.min(page, pages);
+ rows.forEach((row) => {
+ row.hidden = true;
+ });
+ filtered.forEach((row, i) => {
+ row.hidden = i < (page - 1) * size || i >= page * size;
+ body.append(row);
+ });
+ count.textContent = `${filtered.length} ${filtered.length === 1 ? 'entry' : 'entries'} to explore`;
+ document.querySelector('#empty-state')!.hidden =
+ filtered.length !== 0;
+ document.querySelector('#pagination')!.hidden =
+ filtered.length === 0;
+ document.querySelector('#page-info')!.textContent =
+ `Showing ${filtered.length ? (page - 1) * size + 1 : 0}–${Math.min(page * size, filtered.length)} of ${filtered.length} · Page ${page} of ${pages}`;
+ document.querySelector('#year-filter')!.textContent = field(
+ 'year',
+ ).value
+ ? `Created in ${field('year').value}`
+ : '';
+ previous.disabled = page === 1;
+ next.disabled = page === pages;
+ if (sync) {
+ const params = new URLSearchParams();
+ controls.forEach((name) => {
+ const value = field(name).value;
+ if (value && !(name === 'sort' && value === 'name'))
+ params.set(name, value);
+ });
+ if (page > 1) params.set('page', String(page));
+ history.replaceState(
+ null,
+ '',
+ `${location.pathname}${params.size ? '?' + params : ''}${location.hash}`,
+ );
+ }
+ }
+ form.addEventListener('submit', (e) => e.preventDefault());
+ form.addEventListener('input', () => {
+ page = 1;
+ update();
+ });
+ form.addEventListener('change', (event) => {
+ // Search already updates on input. Reordering rows again on blur can cancel
+ // a link click between pointer-down and pointer-up.
+ if (event.target instanceof HTMLInputElement) return;
+ page = 1;
+ update();
+ });
+ function reset() {
+ controls.forEach((name) => {
+ field(name).value = name === 'sort' ? 'name' : '';
+ });
+ page = 1;
+ update();
+ }
+ form.addEventListener('reset', (e) => {
+ e.preventDefault();
+ reset();
+ });
+ document.querySelector('#clear-empty')!.addEventListener('click', reset);
+ document
+ .querySelectorAll('[data-query]')
+ .forEach((button) =>
+ button.addEventListener('click', () => {
+ field('q').value = button.dataset.query!;
+ page = 1;
+ update();
+ }),
+ );
+ previous.addEventListener('click', () => {
+ page--;
+ update();
+ });
+ next.addEventListener('click', () => {
+ page++;
+ update();
+ });
+ window.addEventListener('popstate', () => {
+ load();
+ update(false);
+ });
+ document.addEventListener('keydown', (e) => {
+ if (
+ e.key === '/' &&
+ !e.ctrlKey &&
+ !e.metaKey &&
+ !(
+ e.target instanceof HTMLInputElement ||
+ e.target instanceof HTMLTextAreaElement ||
+ e.target instanceof HTMLSelectElement
+ )
+ ) {
+ e.preventDefault();
+ field('q').focus();
+ }
+ });
+ document
+ .querySelectorAll('[data-sort]')
+ .forEach((button) =>
+ button.addEventListener('click', () => {
+ const key = button.dataset.sort!;
+ const choices =
+ key === 'name'
+ ? ['name', 'reverse']
+ : key === 'detections'
+ ? ['detections', 'detections-asc']
+ : [key, key + '-desc'];
+ field('sort').value =
+ field('sort').value === choices[0] ? choices[1] : choices[0];
+ page = 1;
+ update();
+ }),
+ );
+ load();
+ update(false);
+}
diff --git a/website/src/styles/catalog.css b/website/src/styles/catalog.css
new file mode 100644
index 000000000..3fb79f4c5
--- /dev/null
+++ b/website/src/styles/catalog.css
@@ -0,0 +1,261 @@
+.search-box {
+ display: flex;
+ align-items: center;
+ padding: 15px 19px;
+ gap: 12px;
+ border-bottom: 1px solid var(--line);
+ background: var(--panel-deep);
+}
+.search-box > span {
+ color: var(--accent);
+ font-size: 28px;
+ line-height: 20px;
+}
+.search-box input {
+ width: 100%;
+ background: transparent;
+ color: white;
+ border: 0;
+ outline-offset: 3px;
+ font-size: 13px;
+ padding: 5px;
+}
+.search-box input::placeholder {
+ color: #92a5b4;
+}
+.search-box kbd {
+ border: 1px solid var(--line);
+ padding: 2px 7px;
+ font: 11px var(--mono);
+ color: var(--muted);
+ border-radius: 3px;
+}
+.filters {
+ display: flex;
+ gap: 13px;
+ align-items: end;
+ padding: 16px 19px;
+ border-bottom: 1px solid var(--line);
+}
+.filters label {
+ flex: 1;
+ min-width: 0;
+ color: var(--muted);
+ font: 8px var(--mono);
+ letter-spacing: 1px;
+}
+.filters select {
+ width: 100%;
+ display: block;
+ margin-top: 7px;
+ background: var(--bg);
+ color: var(--text);
+ border: 1px solid var(--line);
+ border-radius: 5px;
+ font:
+ 11px -apple-system,
+ BlinkMacSystemFont,
+ 'Segoe UI',
+ sans-serif;
+ height: 34px;
+ padding: 0 9px;
+}
+.reset {
+ background: transparent;
+ border: 0;
+ color: var(--muted);
+ height: 34px;
+ font-size: 11px;
+ padding: 0 5px;
+}
+.results-toolbar {
+ padding: 16px 19px;
+ display: flex;
+ align-items: center;
+ gap: 15px;
+ font-size: 11px;
+}
+.results-toolbar b {
+ color: white;
+}
+.hint {
+ margin-left: auto;
+ color: var(--muted);
+ font-size: 10px;
+}
+.hint button {
+ padding: 0;
+ border: 0;
+ background: none;
+ color: var(--accent);
+ font: 10px var(--mono);
+}
+.table-scroll {
+ overflow: auto;
+}
+.catalog-table {
+ width: 100%;
+ border-collapse: collapse;
+ text-align: left;
+}
+.catalog-table th {
+ background: #29343f;
+ color: var(--muted);
+ font: 8px var(--mono);
+ letter-spacing: 1px;
+ padding: 12px 14px;
+ white-space: nowrap;
+}
+.catalog-table td {
+ border-bottom: 1px solid #35434e80;
+ padding: 16px 14px;
+ font-size: 11px;
+}
+.catalog-table th:first-child,
+.catalog-table td:first-child {
+ padding-left: 20px;
+ width: 33%;
+}
+.catalog-table td:nth-child(2) {
+ width: 25%;
+}
+.catalog-table tr:hover td {
+ background: #2b374280;
+}
+.entry-name {
+ display: flex;
+ gap: 13px;
+ align-items: center;
+}
+.entry-name strong {
+ font: 600 12px var(--mono);
+ color: var(--accent);
+}
+.entry-name small {
+ display: block;
+ margin-top: 6px;
+ max-width: 250px;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ font-size: 10px;
+ color: var(--muted);
+}
+.badges {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 5px;
+}
+.badge {
+ border: 1px solid #486170;
+ padding: 4px 6px;
+ line-height: 1;
+ color: #a4c4d6;
+ background: #31455260;
+ border-radius: 3px;
+ font-size: 9px;
+ white-space: nowrap;
+}
+.badge.lime {
+ color: #5ad5f3;
+ border-color: #28697b;
+ background: #22c8ef0c;
+}
+.badge.blue {
+ color: #8dbcf6;
+ border-color: #435d7e;
+ background: #8dbcf60c;
+}
+.badge.purple {
+ color: #c2acf2;
+ border-color: #635879;
+ background: #c2acf20c;
+}
+.type-label {
+ font-size: 10px;
+ color: var(--muted);
+}
+.techniques {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 5px;
+ max-width: 160px;
+ color: #a6bfce;
+ font: 10px var(--mono);
+}
+.coverage {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ color: var(--muted);
+ font: 10px var(--mono);
+}
+.coverage i {
+ width: 5px;
+ height: 5px;
+ background: #627482;
+ border-radius: 50%;
+}
+.coverage.available {
+ color: #59cee8;
+}
+.coverage.available i {
+ background: #59cee8;
+}
+.row-arrow {
+ color: #7793a7;
+}
+.pagination {
+ padding: 18px 20px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ font-size: 10px;
+ color: var(--muted);
+}
+.pagination > div {
+ display: flex;
+ gap: 8px;
+}
+.pagination button {
+ background: var(--bg);
+ border: 1px solid var(--line);
+ padding: 7px 12px;
+ border-radius: 5px;
+ color: var(--text);
+ font-size: 10px;
+}
+.empty {
+ text-align: center;
+ padding: 50px 20px;
+}
+.empty p {
+ color: var(--muted);
+ font-size: 13px;
+}
+.catalog-table th button {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 0;
+ border: 0;
+ background: transparent;
+ color: inherit;
+ font: inherit;
+ letter-spacing: inherit;
+ text-align: left;
+}
+.catalog-table th button:hover {
+ color: var(--text);
+}
+.sort-indicator {
+ color: #7895a8;
+ font-size: 11px;
+ letter-spacing: 0;
+}
+.catalog-table th[aria-sort='ascending'] button,
+.catalog-table th[aria-sort='descending'] button,
+.catalog-table th[aria-sort='ascending'] .sort-indicator,
+.catalog-table th[aria-sort='descending'] .sort-indicator {
+ color: var(--accent);
+}
diff --git a/website/src/styles/detail.css b/website/src/styles/detail.css
new file mode 100644
index 000000000..c43179e8c
--- /dev/null
+++ b/website/src/styles/detail.css
@@ -0,0 +1,698 @@
+.detail-hero {
+ padding: 35px 0 0;
+}
+.detail-hero .breadcrumb {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+ font-size: 10px;
+ line-height: 1.6;
+ margin: 0 0 36px;
+}
+.detail-hero-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 24px;
+}
+.detail-identity {
+ display: flex;
+ align-items: center;
+ gap: 22px;
+ min-width: 0;
+}
+.detail-file-mark {
+ flex-shrink: 0;
+ width: 72px;
+ height: 76px;
+ border: 1px solid #3a6778;
+ border-radius: 13px;
+ display: grid;
+ place-items: center;
+ color: var(--accent);
+ font: 30px var(--mono);
+ background: linear-gradient(135deg, #22c8ef18, #22c8ef03);
+ box-shadow: inset 0 0 25px #22c8ef06;
+}
+.detail-identity h1 {
+ font: 650 clamp(28px, 3.6vw, 48px)/1.15 var(--mono);
+ letter-spacing: -2px;
+ margin: 12px 0 0;
+ overflow-wrap: anywhere;
+}
+.detail-identity .eyebrow {
+ font-size: 9px;
+ letter-spacing: 1.3px;
+}
+.detail-source {
+ flex-shrink: 0;
+ color: var(--muted);
+}
+.detail-description {
+ max-width: 850px;
+ color: #b6c6d1;
+ line-height: 1.85;
+ font-size: 15px;
+ margin: 25px 0 17px;
+ white-space: pre-line;
+}
+.detail-capabilities {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+.detail-capabilities .badge {
+ font-size: 10px;
+ padding: 7px 10px;
+}
+.detail-stats {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ margin: 32px 0 38px;
+ border-block: 1px solid var(--line);
+ background: linear-gradient(90deg, #22c8ef06, transparent);
+}
+.detail-stats > div {
+ display: flex;
+ align-items: center;
+ gap: 13px;
+ padding: 22px 20px;
+ border-right: 1px solid var(--line);
+}
+.detail-stats > div:first-child {
+ padding-left: 0;
+}
+.detail-stats > div:last-child {
+ border: 0;
+}
+.detail-stats strong {
+ font: 500 27px var(--mono);
+ letter-spacing: -1.5px;
+ color: var(--accent);
+}
+.detail-stats span {
+ font: 8px/1.8 var(--mono);
+ letter-spacing: 1px;
+ color: var(--muted);
+ max-width: 110px;
+}
+.entry-layout {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) 270px;
+ gap: 32px;
+ align-items: start;
+ margin-bottom: 65px;
+}
+.entry-content {
+ min-width: 0;
+}
+.entry-section {
+ margin: 0 0 36px;
+ scroll-margin-top: 25px;
+}
+.entry-section-heading {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 14px;
+ margin-bottom: 20px;
+}
+.entry-section-heading h2 {
+ font-size: 24px;
+ letter-spacing: -0.8px;
+ margin: 8px 0 0;
+}
+.entry-section-heading .eyebrow {
+ font-size: 8px;
+ letter-spacing: 1.2px;
+}
+.entry-count {
+ color: var(--muted);
+ font: 10px var(--mono);
+ white-space: nowrap;
+}
+.command-group {
+ scroll-margin-top: 25px;
+}
+.category-heading {
+ font-size: 13px;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin: 25px 0 13px;
+}
+.category-heading > span:last-child {
+ font: 9px var(--mono);
+ color: var(--muted);
+ border: 1px solid var(--line);
+ padding: 2px 5px;
+ border-radius: 3px;
+}
+.category-line {
+ width: 3px;
+ height: 15px;
+ background: var(--accent);
+ border-radius: 2px;
+}
+.entry-command {
+ padding: 22px;
+ margin-bottom: 16px;
+ box-shadow: 0 4px 14px #11182013;
+}
+.command-topline {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 15px;
+}
+.command-topline .eyebrow {
+ font-size: 8px;
+}
+.attack-pill {
+ font: 10px var(--mono);
+ color: var(--accent);
+ display: inline-flex;
+ gap: 15px;
+ border: 1px solid #22c8ef35;
+ border-radius: 4px;
+ background: #22c8ef08;
+ padding: 5px 7px;
+}
+.entry-command h4 {
+ font-size: 14px;
+ font-weight: 450;
+ line-height: 1.8;
+ margin: 17px 0;
+ color: #d6e0e6;
+ white-space: pre-line;
+}
+.entry-command .code-wrap {
+ border-color: #354855;
+ background: #182129;
+}
+.command-terminal-label {
+ font: 8px var(--mono);
+ letter-spacing: 1.5px;
+ color: #839dab;
+ padding: 13px 15px 0;
+}
+.command-terminal-label span {
+ color: var(--accent);
+ margin-right: 7px;
+}
+.entry-command pre {
+ font-size: 12px;
+ line-height: 1.9;
+ color: #d4edf4;
+ padding: 14px 17px 18px;
+ tab-size: 2;
+}
+.entry-command .code-wrap button {
+ padding: 5px 9px;
+}
+.entry-command-meta {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 18px;
+ margin: 20px 0 0;
+}
+.entry-command-meta dt,
+.entry-metadata dt {
+ font: 8px/1.5 var(--mono);
+ letter-spacing: 1px;
+ color: #90a5b4;
+ margin-bottom: 6px;
+}
+.entry-command-meta dd {
+ font-size: 11px;
+ line-height: 1.8;
+ margin: 0;
+ color: #d3dde4;
+ white-space: pre-line;
+}
+.entry-command-meta .command-usecase {
+ grid-column: 1/-1;
+}
+.command-tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+ border-top: 1px solid var(--line);
+ margin-top: 18px;
+ padding-top: 15px;
+}
+.command-tags .badge {
+ font-size: 9px;
+ line-height: 1.5;
+ white-space: normal;
+}
+.command-tags .badge > span {
+ color: #d3e5ee;
+}
+.entry-paths {
+ padding: 7px 20px;
+}
+.entry-path {
+ display: flex;
+ gap: 14px;
+ padding: 16px 0;
+ border-bottom: 1px solid var(--line);
+}
+.entry-path:last-child {
+ border: 0;
+}
+.entry-path > span {
+ color: var(--accent);
+}
+.entry-path code {
+ font-size: 11px;
+ line-height: 1.9;
+ overflow-wrap: anywhere;
+ color: #bfd0dc;
+}
+.entry-resource-list {
+ list-style: none;
+ margin: 0;
+ padding: 0 20px;
+}
+.entry-resource-list li + li {
+ border-top: 1px solid var(--line);
+}
+.entry-resource-list li > a {
+ display: flex;
+ align-items: center;
+ gap: 15px;
+ padding: 18px 0;
+ min-width: 0;
+}
+.resource-kind {
+ font: 8px var(--mono);
+ letter-spacing: 0.4px;
+ color: var(--accent);
+ background: #22c8ef0a;
+ border: 1px solid #22c8ef2b;
+ border-radius: 4px;
+ padding: 6px 7px;
+ min-width: 56px;
+ text-align: center;
+ flex-shrink: 0;
+}
+.resource-text {
+ min-width: 0;
+}
+.resource-text strong {
+ display: block;
+ font-size: 12px;
+ font-weight: 500;
+ line-height: 1.7;
+ overflow-wrap: anywhere;
+}
+.resource-text small {
+ font: 9px var(--mono);
+ color: var(--muted);
+ display: block;
+ margin-top: 5px;
+}
+.resource-arrow {
+ margin-left: auto;
+ color: var(--accent);
+ flex-shrink: 0;
+}
+.indicator-panel {
+ margin-top: 15px;
+ padding: 20px;
+ border-left: 2px solid #22c8ef80;
+ background: linear-gradient(120deg, #22c8ef07, var(--panel));
+}
+.indicator-panel h3 {
+ font-size: 13px;
+ font-weight: 600;
+ margin: 0 0 20px;
+}
+.indicator-panel ul {
+ padding: 0;
+ margin: 0;
+ list-style: none;
+}
+.indicator-panel li + li {
+ border-top: 1px solid var(--line);
+ padding-top: 15px;
+ margin-top: 15px;
+}
+.indicator-panel .eyebrow {
+ font-size: 8px;
+}
+.indicator-panel p {
+ font-size: 12px;
+ line-height: 1.9;
+ white-space: pre-line;
+ overflow-wrap: anywhere;
+ margin: 6px 0 0;
+ color: #bfced8;
+}
+.indicator-panel a,
+.entry-empty a {
+ color: var(--accent);
+}
+.entry-empty {
+ padding: 20px;
+ color: var(--muted);
+ font-size: 12px;
+ line-height: 1.8;
+}
+.entry-empty p {
+ margin-top: 0;
+}
+.entry-credits {
+ padding: 22px;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 23px;
+}
+.entry-credits > div {
+ display: flex;
+ gap: 12px;
+ align-items: center;
+ min-width: 0;
+}
+.credit-avatar {
+ width: 32px;
+ height: 32px;
+ flex-shrink: 0;
+ display: grid;
+ place-items: center;
+ border: 1px solid #456171;
+ background: #22c8ef08;
+ border-radius: 50%;
+ color: var(--accent);
+ font: 12px var(--mono);
+}
+.entry-credits strong {
+ font-size: 11px;
+ font-weight: 550;
+ overflow-wrap: anywhere;
+}
+.entry-credits div > div > span {
+ display: block;
+ color: var(--muted);
+ font: 10px var(--mono);
+ margin-top: 5px;
+ overflow-wrap: anywhere;
+}
+.entry-aside {
+ position: sticky;
+ top: 24px;
+}
+.entry-toc {
+ padding: 23px;
+}
+.entry-toc > .eyebrow {
+ font-size: 9px;
+ margin-bottom: 20px;
+}
+.entry-toc > a {
+ display: block;
+ font-size: 11px;
+ color: #b2c1cd;
+ line-height: 1.7;
+ padding: 8px 0;
+}
+.entry-toc > a:hover {
+ color: var(--accent);
+}
+.entry-toc .toc-main {
+ display: flex;
+ justify-content: space-between;
+ color: var(--text);
+}
+.toc-main > span {
+ font: 9px var(--mono);
+ color: var(--muted);
+}
+.entry-toc .toc-category {
+ padding: 6px 0 6px 15px;
+ border-left: 1px solid #22c8ef60;
+ margin-left: 2px;
+ color: #92aebf;
+ font-size: 10px;
+}
+.entry-toc .toc-category:last-of-type {
+ margin-bottom: 6px;
+}
+.entry-metadata {
+ margin-top: 18px;
+ padding: 23px;
+}
+.entry-metadata > .eyebrow {
+ font-size: 9px;
+}
+.entry-metadata dl {
+ margin: 23px 0;
+}
+.entry-metadata dl > div + div {
+ margin-top: 18px;
+}
+.entry-metadata dd {
+ margin: 0;
+ font-size: 12px;
+ line-height: 1.6;
+ overflow-wrap: anywhere;
+}
+.entry-mitre {
+ border-top: 1px solid var(--line);
+ padding-top: 18px;
+}
+.entry-mitre > .eyebrow {
+ font-size: 8px;
+}
+.entry-mitre > div {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+ margin-top: 12px;
+}
+.entry-edit {
+ display: flex;
+ justify-content: space-between;
+ border-top: 1px solid var(--line);
+ margin-top: 22px;
+ padding-top: 18px;
+ font-size: 11px;
+ color: var(--accent);
+}
+.entry-related {
+ padding: 25px 4px;
+}
+.entry-related > .eyebrow {
+ font-size: 8px;
+ letter-spacing: 0.8px;
+}
+.entry-related h2 {
+ font-size: 17px;
+ letter-spacing: -0.4px;
+ margin: 9px 0 13px;
+}
+.entry-related > a {
+ display: flex;
+ justify-content: space-between;
+ gap: 12px;
+ padding: 13px 0;
+ border-bottom: 1px solid var(--line);
+ font: 11px var(--mono);
+ color: var(--muted);
+ overflow-wrap: anywhere;
+}
+.entry-related > a:hover,
+.entry-related > a > span:last-child {
+ color: var(--accent);
+}
+@media (max-width: 1050px) {
+ .entry-layout {
+ grid-template-columns: minmax(0, 1fr) 230px;
+ gap: 22px;
+ }
+ .detail-stats > div {
+ padding: 18px 13px;
+ gap: 10px;
+ }
+ .detail-stats span {
+ font-size: 7px;
+ }
+ .entry-toc,
+ .entry-metadata {
+ padding: 19px;
+ }
+ .detail-file-mark {
+ width: 60px;
+ height: 64px;
+ }
+ .detail-identity h1 {
+ font-size: 35px;
+ }
+ .detail-identity .eyebrow {
+ font-size: 8px;
+ }
+}
+@media (max-width: 760px) {
+ .detail-hero {
+ padding-top: 24px;
+ }
+ .detail-hero .breadcrumb {
+ font-size: 9px;
+ margin-bottom: 26px;
+ gap: 8px;
+ }
+ .detail-hero-row {
+ align-items: start;
+ flex-direction: column;
+ gap: 20px;
+ }
+ .detail-identity {
+ gap: 14px;
+ width: 100%;
+ }
+ .detail-identity h1 {
+ font-size: clamp(23px, 6vw, 35px);
+ letter-spacing: -1px;
+ }
+ .detail-file-mark {
+ width: 46px;
+ height: 50px;
+ font-size: 21px;
+ border-radius: 8px;
+ }
+ .detail-identity .eyebrow {
+ font-size: 7px;
+ letter-spacing: 0.65px;
+ }
+ .detail-source {
+ padding: 9px 12px;
+ font-size: 10px;
+ }
+ .detail-description {
+ font-size: 13px;
+ margin-top: 20px;
+ }
+ .detail-stats {
+ grid-template-columns: 1fr 1fr;
+ margin: 25px 0;
+ }
+ .detail-stats > div,
+ .detail-stats > div:first-child {
+ padding: 16px 10px;
+ }
+ .detail-stats > div:nth-child(2) {
+ border-right: 0;
+ }
+ .detail-stats > div:nth-child(-n + 2) {
+ border-bottom: 1px solid var(--line);
+ }
+ .detail-stats strong {
+ font-size: 25px;
+ }
+ .detail-stats span {
+ font-size: 7px;
+ }
+ .entry-layout {
+ display: flex;
+ flex-direction: column;
+ gap: 25px;
+ margin-bottom: 35px;
+ }
+ .entry-aside {
+ position: static;
+ display: contents;
+ }
+ .entry-toc {
+ order: -1;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 4px 16px;
+ width: 100%;
+ padding: 17px;
+ }
+ .entry-toc > .eyebrow {
+ width: 100%;
+ margin: 0 0 8px;
+ }
+ .entry-toc > a,
+ .entry-toc .toc-category {
+ padding: 4px 0;
+ border: 0;
+ margin: 0;
+ font-size: 10px;
+ }
+ .entry-toc .toc-main > span {
+ margin-left: 8px;
+ }
+ .entry-metadata {
+ width: 100%;
+ margin: 0;
+ }
+ .entry-metadata dl {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 20px;
+ }
+ .entry-metadata dl > div + div {
+ margin: 0;
+ }
+ .entry-related {
+ width: 100%;
+ padding: 0 4px;
+ }
+ .entry-content {
+ width: 100%;
+ }
+ .entry-section-heading h2 {
+ font-size: 22px;
+ }
+ .entry-section-heading .eyebrow {
+ font-size: 7px;
+ }
+ .entry-count {
+ font-size: 9px;
+ }
+ .entry-command {
+ padding: 16px;
+ }
+ .entry-command h4 {
+ font-size: 12px;
+ }
+ .entry-command pre {
+ font-size: 11px;
+ padding: 15px 13px;
+ }
+ .entry-command-meta {
+ gap: 16px;
+ }
+ .entry-paths {
+ padding: 5px 14px;
+ }
+ .entry-path code {
+ font-size: 10px;
+ }
+ .entry-resource-list {
+ padding: 0 15px;
+ }
+ .entry-resource-list li > a {
+ gap: 10px;
+ }
+ .resource-kind {
+ min-width: 46px;
+ font-size: 7px;
+ padding: 5px;
+ }
+ .resource-text strong {
+ font-size: 11px;
+ }
+ .entry-credits {
+ padding: 18px;
+ gap: 20px;
+ grid-template-columns: 1fr;
+ }
+ .entry-section {
+ margin-bottom: 28px;
+ }
+}
diff --git a/website/src/styles/filters.css b/website/src/styles/filters.css
new file mode 100644
index 000000000..23e2b4bdc
--- /dev/null
+++ b/website/src/styles/filters.css
@@ -0,0 +1,102 @@
+/* Progressive native select styling keeps OS controls on older browsers. */
+.filters select {
+ transition:
+ border-color 0.15s,
+ background-color 0.15s;
+}
+.filters select:hover {
+ border-color: #5c7b8c;
+ background-color: #24323d;
+}
+@supports (appearance: base-select) {
+ .filters select,
+ .filters select::picker(select) {
+ appearance: base-select;
+ }
+ .filters select {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+ height: 36px;
+ padding: 0 11px;
+ border-radius: 6px;
+ font-size: 11px;
+ overflow: hidden;
+ white-space: nowrap;
+ }
+ .filters select:open {
+ border-color: var(--accent);
+ box-shadow: 0 0 0 3px #22c8ef12;
+ background: #253741;
+ }
+ .filters select::picker-icon {
+ content: '⌄';
+ color: #8fa8b8;
+ font-size: 15px;
+ margin-left: auto;
+ transition: rotate 0.15s;
+ }
+ .filters select:open::picker-icon {
+ rotate: 180deg;
+ color: var(--accent);
+ }
+ .filters select::picker(select) {
+ margin: 8px 0 0;
+ padding: 6px;
+ color: var(--text);
+ background: #222e38;
+ border: 1px solid #466272;
+ border-radius: 9px;
+ box-shadow:
+ 0 16px 45px #10161dcc,
+ 0 0 0 1px #22c8ef09;
+ min-width: anchor-size(width);
+ max-height: min(340px, 65vh);
+ scrollbar-width: thin;
+ scrollbar-color: #4a6677 transparent;
+ font:
+ 12px/1.4 -apple-system,
+ BlinkMacSystemFont,
+ 'Segoe UI',
+ sans-serif;
+ }
+ .filters option {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ min-height: 34px;
+ padding: 8px 10px;
+ border-radius: 5px;
+ color: #c2d0da;
+ cursor: pointer;
+ }
+ .filters option:first-child {
+ color: #8fa8b8;
+ }
+ .filters option:hover,
+ .filters option:focus {
+ background: #2e4350;
+ color: #effaff;
+ outline: none;
+ }
+ .filters option:checked {
+ background: #22c8ef14;
+ color: #63d9f3;
+ font-weight: 600;
+ }
+ .filters option::checkmark {
+ content: '✓';
+ color: var(--accent);
+ font-size: 11px;
+ }
+ .filters option + option {
+ margin-top: 2px;
+ }
+}
+@media (prefers-reduced-motion: reduce) {
+ .filters select,
+ .filters select::picker-icon {
+ transition: none;
+ }
+}
diff --git a/website/src/styles/global.css b/website/src/styles/global.css
new file mode 100644
index 000000000..0ffed990a
--- /dev/null
+++ b/website/src/styles/global.css
@@ -0,0 +1,823 @@
+@import './catalog.css';
+@import './pages.css';
+:root {
+ color-scheme: dark;
+ --bg: #1e262e;
+ --panel: #222c35;
+ --panel-deep: #1a222a;
+ --line: #35434e;
+ --text: #f3f6f9;
+ --muted: #9baeba;
+ --accent: #22c8ef;
+ --mono: ui-monospace, SFMono-Regular, Consolas, monospace;
+ font-family:
+ Inter,
+ -apple-system,
+ BlinkMacSystemFont,
+ 'Segoe UI',
+ sans-serif;
+ background: var(--bg);
+ color: var(--text);
+}
+* {
+ box-sizing: border-box;
+}
+body {
+ margin: 0;
+}
+a {
+ color: inherit;
+ text-decoration: none;
+}
+button,
+input,
+select {
+ font: inherit;
+}
+button,
+a,
+input,
+select,
+summary {
+ outline-offset: 5px;
+}
+a:hover {
+ color: var(--accent);
+}
+button {
+ cursor: pointer;
+}
+button:disabled {
+ cursor: default;
+ opacity: 0.4;
+}
+button:focus-visible,
+a:focus-visible,
+input:focus-visible,
+select:focus-visible,
+summary:focus-visible {
+ outline: 2px solid var(--accent);
+}
+[hidden] {
+ display: none !important;
+}
+::selection {
+ background: #22c8ef55;
+}
+html {
+ scroll-behavior: smooth;
+ scroll-padding-top: 20px;
+}
+.header,
+main,
+footer {
+ max-width: 1360px;
+ margin: auto;
+ padding-left: 48px;
+ padding-right: 48px;
+}
+.header {
+ height: 88px;
+ display: flex;
+ align-items: center;
+ gap: 64px;
+ border-bottom: 1px solid var(--line);
+}
+.brand {
+ font-weight: 850;
+ font-size: 25px;
+ letter-spacing: -1px;
+ display: inline-flex;
+ align-items: center;
+ gap: 10px;
+}
+.brand-logo {
+ object-fit: contain;
+ /* Keep the original badge legible on a dark surface without enlarging it. */
+ filter: invert(1) hue-rotate(180deg);
+}
+.brand-dot {
+ color: var(--accent);
+}
+.header nav {
+ display: flex;
+ gap: 32px;
+ font-size: 14px;
+ color: var(--muted);
+}
+.header nav a:hover {
+ color: white;
+}
+.repo-link {
+ display: inline-flex;
+ align-items: center;
+ gap: 9px;
+ margin-left: auto;
+ border: 1px solid var(--line);
+ padding: 9px 16px;
+ border-radius: 6px;
+ font-size: 13px;
+}
+.repo-link span {
+ color: var(--accent);
+ margin-left: 6px;
+}
+.eyebrow {
+ display: block;
+ font-family: var(--mono);
+ font-size: 10px;
+ letter-spacing: 1.6px;
+ color: var(--muted);
+ font-weight: 500;
+ line-height: 1.5;
+}
+.status-dot {
+ display: inline-block;
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: var(--accent);
+ box-shadow: 0 0 10px #22c8ef55;
+ vertical-align: middle;
+ margin-right: 8px;
+}
+.hero {
+ display: grid;
+ grid-template-columns: 1.3fr 1fr;
+ gap: 20px;
+ padding: 55px 0 42px;
+ align-items: center;
+}
+.hero h1 {
+ font-size: clamp(34px, 3.65vw, 52px);
+ line-height: 1.1;
+ letter-spacing: -2.8px;
+ margin: 21px 0;
+}
+.hero h1 span {
+ color: var(--accent);
+}
+.intro {
+ color: var(--muted);
+ font-size: 15px;
+ line-height: 1.85;
+}
+.hero-actions {
+ display: flex;
+ align-items: center;
+ gap: 25px;
+ margin-top: 28px;
+}
+.button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 28px;
+ padding: 13px 18px;
+ border: 1px solid var(--line);
+ border-radius: 6px;
+ background: transparent;
+ color: var(--text);
+ font-size: 12px;
+ font-weight: 650;
+}
+.primary {
+ background: var(--accent);
+ color: #15222b;
+ border-color: var(--accent);
+}
+.primary:hover {
+ background: #6addf7;
+ color: #15222b;
+}
+.text-link {
+ font-size: 12px;
+ color: var(--muted);
+}
+.text-link span {
+ color: var(--accent);
+ margin-left: 10px;
+}
+.hero-art {
+ height: 290px;
+ position: relative;
+ display: flex;
+ justify-content: center;
+}
+.radar {
+ position: relative;
+ height: 300px;
+ width: 360px;
+ background: radial-gradient(ellipse, #22c8ef0c, transparent 70%);
+}
+.orbit {
+ border: 1px solid #22c8ef20;
+ border-radius: 50%;
+ position: absolute;
+ inset: 0 30px;
+}
+.orbit.two {
+ inset: 30px 60px;
+}
+.orbit.three {
+ inset: 60px 90px;
+}
+.crosshair {
+ position: absolute;
+ background: #22c8ef15;
+}
+.horizontal {
+ width: 100%;
+ height: 1px;
+ top: 50%;
+}
+.vertical {
+ height: 100%;
+ width: 1px;
+ left: 50%;
+}
+.scan {
+ position: absolute;
+ inset: 0 30px;
+ border-radius: 50%;
+ background: conic-gradient(
+ from 40deg,
+ transparent 0deg,
+ #22c8ef0c 65deg,
+ transparent 66deg
+ );
+}
+.terminal {
+ position: absolute;
+ top: 65px;
+ left: 15px;
+ width: 330px;
+ background: linear-gradient(130deg, #2b3742, #19232c);
+ border: 1px solid #466274;
+ border-radius: 9px;
+ box-shadow: 0 25px 55px #10182070;
+ transform: rotate(-3deg);
+}
+.terminal-top {
+ height: 31px;
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ border-bottom: 1px solid var(--line);
+ padding: 0 13px;
+}
+.terminal-top i {
+ width: 5px;
+ height: 5px;
+ border-radius: 50%;
+ background: #657887;
+}
+.terminal-top i:first-child {
+ background: var(--accent);
+}
+.terminal-top span {
+ margin-left: auto;
+ font: 8px var(--mono);
+ letter-spacing: 1px;
+ color: #829caf;
+}
+.terminal p {
+ font: 10px var(--mono);
+ margin: 12px 15px;
+ color: var(--muted);
+}
+.terminal p span {
+ color: var(--accent);
+}
+.terminal-mark {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+.terminal-mark img {
+ display: block;
+ object-fit: contain;
+ filter: invert(1) hue-rotate(180deg) drop-shadow(0 0 14px #22c8ef20);
+}
+.terminal .terminal-bottom {
+ font-size: 7px;
+ letter-spacing: 0.4px;
+ text-align: center;
+ margin: 12px 10px;
+}
+.radar-label {
+ position: absolute;
+ font: 8px var(--mono);
+ color: #6b8799;
+ letter-spacing: 2px;
+}
+.label-top {
+ top: 12px;
+ left: 28px;
+}
+.label-bottom {
+ bottom: 5px;
+ right: 0;
+}
+.radar-point {
+ position: absolute;
+ height: 5px;
+ width: 5px;
+ background: var(--accent);
+ border-radius: 50%;
+ box-shadow: 0 0 0 5px #22c8ef12;
+}
+.p1 {
+ left: 65px;
+ top: 32px;
+}
+.p2 {
+ right: 24px;
+ top: 112px;
+}
+.p3 {
+ left: 115px;
+ bottom: 9px;
+}
+.metrics {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ background: linear-gradient(120deg, #27323c, #222c35);
+}
+.metrics > div {
+ padding: 21px 23px;
+ border-right: 1px solid var(--line);
+}
+.metrics > div:last-child {
+ border: 0;
+}
+.metric-label {
+ font: 9px var(--mono);
+ letter-spacing: 1.2px;
+ color: var(--muted);
+}
+.metrics strong {
+ display: block;
+ font-size: 30px;
+ letter-spacing: -1px;
+ margin-top: 9px;
+}
+.metrics small {
+ display: block;
+ font-size: 11px;
+ color: var(--muted);
+ font-weight: 400;
+ letter-spacing: 0;
+ margin-top: 5px;
+}
+.metrics > div:first-child strong {
+ color: var(--accent);
+}
+.intelligence-grid {
+ display: grid;
+ grid-template-columns: 1.16fr 1fr;
+ gap: 18px;
+ margin-top: 23px;
+}
+.panel {
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ background: var(--panel);
+ overflow: hidden;
+}
+.growth,
+.recent {
+ padding: 22px 25px;
+}
+.panel-heading {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+.panel-heading h2 {
+ font-size: 18px;
+ letter-spacing: -0.4px;
+ margin: 8px 0 0;
+}
+.panel-heading > a {
+ border: 1px solid var(--line);
+ padding: 5px 8px;
+ border-radius: 5px;
+ color: var(--muted);
+}
+.chart-summary {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin: 14px 0 0;
+}
+.chart-summary > strong {
+ font-size: 29px;
+ letter-spacing: -1px;
+}
+.chart-summary > span {
+ font-size: 9px;
+ line-height: 1.6;
+ color: var(--muted);
+}
+.chart-summary b {
+ font-weight: 400;
+}
+.chart-key {
+ margin-left: auto;
+}
+.chart-key i {
+ display: inline-block;
+ width: 12px;
+ height: 2px;
+ background: var(--accent);
+ vertical-align: middle;
+ margin-right: 5px;
+}
+.growth-chart {
+ display: block;
+ width: 100%;
+ height: 120px;
+ margin-top: 0;
+}
+.chart-note {
+ color: var(--muted);
+ font-size: 9px;
+ margin: 10px 0 0;
+}
+.chart-data {
+ font-size: 10px;
+ margin-top: 9px;
+ color: var(--muted);
+}
+.chart-data table {
+ width: 100%;
+ text-align: left;
+ line-height: 2;
+ margin-top: 10px;
+}
+.recent-entry {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 16px 0;
+ border-bottom: 1px solid var(--line);
+}
+.recent-entry h3 {
+ font: 600 12px var(--mono);
+ margin: 0 0 5px;
+}
+.recent-entry p {
+ margin: 0;
+ font-size: 10px;
+ color: var(--muted);
+ max-width: 230px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.recent-entry time {
+ font: 9px var(--mono);
+ color: var(--muted);
+ margin-left: auto;
+ white-space: nowrap;
+}
+.file-icon {
+ font: 15px var(--mono);
+ color: var(--muted);
+ border: 1px solid var(--line);
+ border-radius: 5px;
+ min-width: 30px;
+ height: 32px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ background: #23303b;
+}
+.section-heading {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 40px 0 20px;
+}
+.section-heading h2 {
+ font-size: 30px;
+ letter-spacing: -1.3px;
+ margin: 8px 0;
+}
+.section-heading p {
+ font-size: 12px;
+ color: var(--muted);
+ margin: 0;
+}
+.catalog-section {
+ scroll-margin-top: 20px;
+}
+.contribute-link {
+ margin: 30px 0 48px;
+ text-align: center;
+}
+footer {
+ border-top: 1px solid var(--line);
+ padding-top: 32px;
+ padding-bottom: 40px;
+ display: flex;
+ justify-content: space-between;
+}
+footer p {
+ font-size: 11px;
+ color: var(--muted);
+}
+footer > div:last-child {
+ text-align: right;
+}
+footer a:not(.brand) {
+ font-size: 11px;
+ margin-left: 20px;
+}
+footer small {
+ display: block;
+ color: var(--muted);
+ font-size: 9px;
+ line-height: 1.7;
+ margin-top: 17px;
+}
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+}
+.skip {
+ position: absolute;
+ top: -80px;
+ left: 20px;
+ background: var(--accent);
+ color: #18252e;
+ padding: 10px;
+ z-index: 10;
+}
+.skip:focus {
+ top: 10px;
+}
+@media (min-width: 1450px) {
+ .hero {
+ padding-top: 65px;
+ padding-bottom: 55px;
+ }
+ .hero-art {
+ transform: scale(1.08);
+ }
+}
+@media (max-width: 1000px) {
+ .header,
+ main,
+ footer {
+ padding-left: 25px;
+ padding-right: 25px;
+ }
+ .hero h1 {
+ font-size: 44px;
+ }
+ .desktop-br {
+ display: none;
+ }
+ .hero-art {
+ transform: scale(0.85);
+ }
+ .metrics > div {
+ padding: 18px 15px;
+ }
+ .filters {
+ flex-wrap: wrap;
+ }
+ .filters label {
+ flex: 1 1 25%;
+ }
+ .recent-entry time {
+ display: none;
+ }
+ .entry-name small {
+ max-width: 200px;
+ }
+ .header {
+ gap: 40px;
+ }
+}
+@media (max-width: 700px) {
+ .header,
+ main,
+ footer {
+ padding-left: 20px;
+ padding-right: 20px;
+ }
+ .header {
+ height: 75px;
+ gap: 15px;
+ flex-wrap: wrap;
+ }
+ .brand {
+ font-size: 20px;
+ }
+ .brand-logo {
+ width: 28px;
+ height: 28px;
+ }
+ .header nav {
+ margin-left: auto;
+ gap: 17px;
+ font-size: 11px;
+ }
+ .repo-link {
+ display: none;
+ }
+ .hero {
+ display: block;
+ padding: 35px 0 30px;
+ }
+ .hero h1 {
+ font-size: 38px;
+ letter-spacing: -1.8px;
+ }
+ .hero .eyebrow {
+ font-size: 8px;
+ letter-spacing: 1px;
+ }
+ .hero-art {
+ display: none;
+ }
+ .intro {
+ font-size: 13px;
+ }
+ .hero-actions {
+ gap: 17px;
+ }
+ .hero-actions .text-link {
+ font-size: 10px;
+ }
+ .metrics {
+ grid-template-columns: 1fr 1fr;
+ }
+ .metrics > div {
+ padding: 18px;
+ }
+ .metrics > div:nth-child(2) {
+ border-right: 0;
+ }
+ .metrics > div:nth-child(-n + 2) {
+ border-bottom: 1px solid var(--line);
+ }
+ .metrics > div:last-child {
+ grid-column: 1 / -1;
+ }
+ .metric-label {
+ font-size: 8px;
+ }
+ .metrics strong {
+ font-size: 26px;
+ }
+ .metrics small {
+ font-size: 9px;
+ }
+ .intelligence-grid,
+ .insights-grid {
+ grid-template-columns: 1fr;
+ }
+ .growth,
+ .recent {
+ padding: 20px;
+ }
+ .recent-entry time {
+ display: block;
+ font-size: 8px;
+ }
+ .recent-entry p {
+ max-width: 185px;
+ }
+ .chart-key {
+ display: none;
+ }
+ .section-heading {
+ padding-top: 30px;
+ }
+ .section-heading h2 {
+ font-size: 25px;
+ }
+ .section-heading .text-link {
+ font-size: 10px;
+ }
+ .section-heading .eyebrow {
+ font-size: 8px;
+ }
+ .filters {
+ padding: 14px;
+ gap: 10px;
+ }
+ .filters label {
+ flex: 1 1 40%;
+ }
+ .filters label:nth-child(5) {
+ flex: 1 1 65%;
+ }
+ .hint {
+ display: none;
+ }
+ .results-toolbar {
+ padding: 14px;
+ }
+ .catalog-table {
+ min-width: 690px;
+ }
+ .catalog-table td {
+ padding: 14px 12px;
+ }
+ .entry-name small {
+ max-width: 185px;
+ }
+ .catalog-table th {
+ font-size: 7px;
+ }
+ .pagination {
+ padding: 15px 12px;
+ gap: 10px;
+ }
+ .pagination button {
+ padding: 7px;
+ }
+ footer {
+ gap: 20px;
+ flex-direction: column;
+ }
+ footer > div:last-child {
+ text-align: left;
+ }
+ footer a:not(.brand) {
+ margin: 0 15px 0 0;
+ }
+ .page-head {
+ padding: 35px 0 20px;
+ }
+ .page-head h1 {
+ font-size: 34px;
+ }
+ .page-head p {
+ font-size: 13px;
+ }
+ .detail-grid {
+ display: flex;
+ flex-direction: column-reverse;
+ gap: 20px;
+ }
+ .detail-grid aside {
+ position: static;
+ }
+ .detail-sidebar ul {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+ }
+ .command-card {
+ padding: 16px;
+ }
+ .command-meta {
+ grid-template-columns: 1fr;
+ }
+ .bar-row {
+ grid-template-columns: 110px 1fr 25px;
+ gap: 8px;
+ }
+}
+@media (prefers-reduced-motion: reduce) {
+ html {
+ scroll-behavior: auto;
+ }
+}
+
+/* Keep grid children within the viewport and chart links easy to target. */
+.panel {
+ min-width: 0;
+}
+.intelligence-grid > *,
+.insights-grid > * {
+ min-width: 0;
+}
+.recent-entry > div {
+ min-width: 0;
+}
+.growth-chart path,
+.growth-chart line {
+ pointer-events: none;
+}
+.growth-chart a:hover circle {
+ r: 6px;
+ fill: #a2ecff;
+}
+.chart-data a {
+ color: var(--accent);
+}
+.catalog-table th {
+ position: relative;
+}
diff --git a/website/src/styles/pages.css b/website/src/styles/pages.css
new file mode 100644
index 000000000..65a359722
--- /dev/null
+++ b/website/src/styles/pages.css
@@ -0,0 +1,230 @@
+.page-head {
+ padding: 50px 0 30px;
+}
+.breadcrumb {
+ color: var(--muted);
+ font: 11px var(--mono);
+ margin-bottom: 28px;
+}
+.breadcrumb a {
+ color: var(--accent);
+}
+.page-head h1 {
+ font-size: 44px;
+ letter-spacing: -2px;
+ margin: 12px 0;
+}
+.page-head p {
+ color: var(--muted);
+ font-size: 15px;
+ line-height: 1.8;
+ max-width: 780px;
+}
+.detail-grid {
+ display: grid;
+ grid-template-columns: 1fr 280px;
+ gap: 26px;
+ margin-bottom: 60px;
+}
+.detail-grid aside {
+ align-self: start;
+ position: sticky;
+ top: 20px;
+}
+.detail-sidebar {
+ padding: 22px;
+ font-size: 12px;
+}
+.detail-sidebar h2 {
+ font-size: 13px;
+}
+.detail-sidebar ul {
+ padding: 0;
+ list-style: none;
+ line-height: 2.3;
+}
+.detail-sidebar dl {
+ font-size: 11px;
+ line-height: 1.9;
+}
+.detail-sidebar dt {
+ color: var(--muted);
+}
+.detail-sidebar dd {
+ margin: 0 0 10px;
+}
+.content-section {
+ margin-bottom: 30px;
+}
+.content-section h2 {
+ font-size: 23px;
+ letter-spacing: -0.5px;
+}
+.command-card {
+ padding: 22px;
+ margin-bottom: 15px;
+}
+.command-card h3 {
+ font-size: 14px;
+ font-weight: 550;
+ line-height: 1.7;
+ margin: 0 0 14px;
+}
+.code-wrap {
+ position: relative;
+ border: 1px solid var(--line);
+ border-radius: 5px;
+ background: var(--panel-deep);
+}
+pre {
+ font: 12px/1.8 var(--mono);
+ white-space: pre-wrap;
+ overflow-wrap: anywhere;
+ padding: 17px;
+ margin: 0;
+}
+code {
+ font-family: var(--mono);
+}
+.code-wrap pre {
+ padding-right: 70px;
+}
+.code-wrap button {
+ position: absolute;
+ right: 9px;
+ top: 10px;
+ background: var(--panel);
+ color: var(--muted);
+ font-size: 10px;
+ border: 1px solid var(--line);
+ border-radius: 3px;
+ padding: 5px 8px;
+}
+.command-meta {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 14px;
+ font-size: 11px;
+ line-height: 1.7;
+ margin-bottom: 0;
+}
+.command-meta dt {
+ color: var(--muted);
+ font: 8px var(--mono);
+ letter-spacing: 1px;
+ margin-bottom: 5px;
+}
+.command-meta dd {
+ margin: 0;
+}
+.command-meta a,
+.resource-list a {
+ color: var(--accent);
+}
+.resource-list {
+ padding: 20px 25px;
+ list-style: none;
+ margin: 0;
+}
+.resource-list li {
+ padding: 10px 0;
+ border-bottom: 1px solid var(--line);
+ font-size: 12px;
+ line-height: 1.8;
+ overflow-wrap: anywhere;
+}
+.resource-list li:last-child {
+ border: 0;
+}
+.resource-list strong {
+ color: var(--muted);
+ font: 10px var(--mono);
+ margin-right: 12px;
+}
+.paths {
+ font: 12px/2 var(--mono);
+ overflow-wrap: anywhere;
+}
+.article {
+ max-width: 820px;
+ margin: 0 auto 70px;
+ font-size: 14px;
+ line-height: 1.9;
+}
+.article h2 {
+ margin-top: 35px;
+ font-size: 24px;
+}
+.article a:not(.button) {
+ color: var(--accent);
+}
+.maintainers h2 {
+ margin-top: 0;
+}
+.maintainer-list {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
+ gap: 0 24px;
+ padding: 0;
+ list-style: none;
+ margin: 20px 0 38px;
+}
+.maintainer-list a {
+ display: flex;
+ flex-direction: column;
+ padding: 12px 0;
+ border-bottom: 1px solid var(--line);
+}
+.maintainer-list a span {
+ color: var(--text);
+}
+.maintainer-list small {
+ font-size: 12px;
+}
+.maintainer-list a:hover span {
+ color: var(--accent);
+}
+.article .panel {
+ padding: 20px;
+}
+.endpoint {
+ font: 14px var(--mono);
+}
+.insights-grid {
+ display: grid;
+ grid-template-columns: 1.15fr 1fr;
+ gap: 20px;
+ margin-bottom: 50px;
+}
+.distribution {
+ padding: 24px;
+}
+.distribution h2 {
+ font-size: 20px;
+ margin-top: 0;
+}
+.bar-row {
+ display: grid;
+ grid-template-columns: 125px 1fr 30px;
+ gap: 15px;
+ align-items: center;
+ font-size: 11px;
+ margin: 16px 0;
+}
+.bar-track {
+ height: 6px;
+ background: var(--line);
+ border-radius: 5px;
+ overflow: hidden;
+}
+.bar-track i {
+ display: block;
+ height: 100%;
+ background: var(--accent);
+ border-radius: 5px;
+}
+.insight-note {
+ color: var(--muted);
+ font-size: 11px;
+ line-height: 1.8;
+}
diff --git a/website/tests/data-contract.test.mjs b/website/tests/data-contract.test.mjs
new file mode 100644
index 000000000..7103cac5b
--- /dev/null
+++ b/website/tests/data-contract.test.mjs
@@ -0,0 +1,284 @@
+import test from 'node:test';
+import assert from 'node:assert/strict';
+import { mkdtemp, mkdir, writeFile, rm } from 'node:fs/promises';
+import { tmpdir } from 'node:os';
+import { join } from 'node:path';
+import {
+ parseEntry,
+ loadCatalog,
+ toApi,
+ toCsv,
+ toNavigator,
+ apiKeys,
+ csvHeader,
+ collections,
+} from '../scripts/prepare-data.mjs';
+
+const fixture = `Name: Example.exe
+Description: 'A "quoted", description'
+Author: Example Author
+Created: 2020-01-02
+Commands:
+ - Command: |-
+ example.exe "first,second"
+ next line
+ Description: A command
+ Usecase: Example
+ Category: Execute
+ Privileges: User
+ MitreID: T1218.001
+ OperatingSystem: Windows 11
+ Tags:
+ - Application: 'GUI, "quoted"'
+ - Network: Remote
+Acknowledgement:
+ - Person: Example
+ Handle: '@example'
+`;
+const record = () =>
+ parseEntry(fixture, 'yml/OSBinaries/Example.yml', 'Binaries', 'Example');
+
+// Independent parser makes commas, embedded quotes and multiline cells observable.
+export function parseCsv(text) {
+ const rows = [];
+ let row = [];
+ let cell = '';
+ let quoted = false;
+ for (let i = 0; i < text.length; i++) {
+ const char = text[i];
+ if (char === '"') {
+ if (quoted && text[i + 1] === '"') {
+ cell += '"';
+ i++;
+ } else quoted = !quoted;
+ } else if (char === ',' && !quoted) {
+ row.push(cell);
+ cell = '';
+ } else if (char === '\n' && !quoted) {
+ row.push(cell);
+ rows.push(row);
+ row = [];
+ cell = '';
+ } else cell += char;
+ }
+ assert.equal(quoted, false, 'CSV quotes must close');
+ if (cell || row.length) {
+ row.push(cell);
+ rows.push(row);
+ }
+ return rows;
+}
+
+test('legacy JSON has exact keys, null missing fields, original command fields, and canonical URL', () => {
+ const entry = record();
+ const [api] = toApi([entry]);
+ assert.deepEqual(Object.keys(api), apiKeys);
+ assert.equal(api.Created, '2020-01-02');
+ assert.equal(api.Full_Path, null);
+ assert.equal(api.Detection, null);
+ assert.equal(api.Resources, null);
+ assert.deepEqual(api.Commands, entry.Commands);
+ assert.equal(
+ api.url,
+ 'https://lolbas-project.github.io/lolbas/Binaries/Example/',
+ );
+ assert.ok(!('Acknowledgement' in api));
+ assert.ok(!('source' in api));
+});
+
+test('CSV preserves 17 legacy columns and round trips multiline commands, quotes, commas and tags', () => {
+ const [header, row] = parseCsv(toCsv([record()]));
+ assert.deepEqual(header, csvHeader);
+ assert.equal(row.length, 17);
+ assert.equal(row[1], 'A "quoted", description');
+ assert.equal(row[4], 'example.exe "first,second"\nnext line');
+ assert.equal(row[14], 'Example (@example)');
+ assert.equal(row[16], 'Application:GUI, "quoted",Network:Remote');
+ assert.equal(row[11], '');
+});
+
+test('rejects malformed YAML, duplicate keys, invalid command shape and unsafe route IDs', () => {
+ for (const text of [
+ 'Name: [',
+ fixture + '\nName: Duplicate',
+ fixture.replace('Category: Execute', 'Category: [Execute]'),
+ fixture.replace('MitreID: T1218.001', 'MitreID: invalid'),
+ ]) {
+ assert.throws(
+ () => parseEntry(text, 'bad.yml', 'Binaries', 'Example'),
+ /bad.yml:/,
+ );
+ }
+ assert.throws(
+ () => parseEntry(fixture, 'bad.yml', 'Binaries', '../escape'),
+ /filename/,
+ );
+});
+
+test('collection mapping excludes HonorableMentions and duplicate binary filenames fail before generation', async () => {
+ const root = await mkdtemp(join(tmpdir(), 'lolbas-data-'));
+ try {
+ for (const folder of [...Object.keys(collections), 'HonorableMentions'])
+ await mkdir(join(root, folder));
+ await writeFile(join(root, 'OSBinaries/Example.yml'), fixture);
+ await writeFile(
+ join(root, 'HonorableMentions/Ignored.yml'),
+ 'malformed: [',
+ );
+ assert.equal((await loadCatalog(root)).length, 1);
+ await writeFile(join(root, 'OtherMSBinaries/eXample.yml'), fixture);
+ await assert.rejects(
+ loadCatalog(root),
+ /duplicate binary filename.*OSBinaries\/Example.yml/,
+ );
+ } finally {
+ await rm(root, { recursive: true, force: true });
+ }
+});
+
+test('extension gate rejects .yaml, uppercase .YML and unrelated files even in unpublished collections', async () => {
+ const root = await mkdtemp(join(tmpdir(), 'lolbas-extension-'));
+ try {
+ for (const folder of [
+ ...Object.keys(collections),
+ 'HonorableMentions',
+ 'FutureCollection',
+ ])
+ await mkdir(join(root, folder));
+ await writeFile(join(root, 'OSBinaries/Example.yml'), fixture);
+ for (const path of [
+ 'OSBinaries/Bad.yaml',
+ 'OSLibraries/Bad.YML',
+ 'HonorableMentions/Bad.txt',
+ 'FutureCollection/Bad.json',
+ 'Bad.md',
+ ]) {
+ await writeFile(join(root, path), fixture);
+ await assert.rejects(
+ loadCatalog(root),
+ /unexpected extension; use '.yml'/,
+ );
+ await rm(join(root, path));
+ }
+ assert.equal((await loadCatalog(root)).length, 1);
+ } finally {
+ await rm(root, { recursive: true, force: true });
+ }
+});
+
+test('distinct binary names and matching library stems are valid', async () => {
+ const root = await mkdtemp(join(tmpdir(), 'lolbas-names-'));
+ try {
+ for (const folder of Object.keys(collections))
+ await mkdir(join(root, folder));
+ for (const path of [
+ 'OSBinaries/Example.yml',
+ 'OtherMSBinaries/Another.yml',
+ 'OSLibraries/Example.yml',
+ ])
+ await writeFile(join(root, path), fixture);
+ assert.equal((await loadCatalog(root)).length, 3);
+ } finally {
+ await rm(root, { recursive: true, force: true });
+ }
+});
+
+test('extension validation is recursive and published collections cannot hide nested entries', async () => {
+ const root = await mkdtemp(join(tmpdir(), 'lolbas-nested-'));
+ try {
+ for (const folder of [
+ ...Object.keys(collections),
+ 'HonorableMentions/nested/deeper',
+ ])
+ await mkdir(join(root, folder), { recursive: true });
+ await writeFile(join(root, 'OSBinaries/Example.yml'), fixture);
+ await writeFile(
+ join(root, 'HonorableMentions/nested/deeper/Bad.txt'),
+ fixture,
+ );
+ await assert.rejects(
+ loadCatalog(root),
+ /HonorableMentions\/nested\/deeper\/Bad.txt: unexpected extension/,
+ );
+ await rm(join(root, 'HonorableMentions/nested/deeper/Bad.txt'));
+ await writeFile(
+ join(root, 'HonorableMentions/nested/deeper/Allowed.yml'),
+ fixture,
+ );
+ assert.equal((await loadCatalog(root)).length, 1);
+ for (const folder of Object.keys(collections)) {
+ await mkdir(join(root, folder, 'nested'));
+ await writeFile(join(root, folder, 'nested/Hidden.yml'), fixture);
+ await assert.rejects(
+ loadCatalog(root),
+ /nested directories are not supported in published collections/,
+ );
+ await rm(join(root, folder, 'nested'), { recursive: true });
+ }
+ } finally {
+ await rm(root, { recursive: true, force: true });
+ }
+});
+
+test('Created rejects impossible dates and accepts leap dates', () => {
+ for (const date of [
+ '2023-02-29',
+ '2020-02-30',
+ '2020-13-01',
+ '2020-00-01',
+ '2020-1-02',
+ ]) {
+ assert.throws(
+ () =>
+ parseEntry(
+ fixture.replace('2020-01-02', date),
+ 'bad.yml',
+ 'Binaries',
+ 'Example',
+ ),
+ /valid calendar date/,
+ );
+ }
+ assert.equal(
+ parseEntry(
+ fixture.replace('2020-01-02', '2020-02-29'),
+ 'good.yml',
+ 'Binaries',
+ 'Example',
+ ).Created,
+ '2020-02-29',
+ );
+});
+
+test('Navigator aggregates every command and marks parents with subtechniques', () => {
+ const entry = record();
+ entry.Commands.push(
+ { ...entry.Commands[0], MitreID: 'T1218' },
+ { ...entry.Commands[0] },
+ );
+ const layer = toNavigator([entry]);
+ assert.deepEqual(layer.techniques, [
+ { techniqueID: 'T1218', score: 1, showSubtechniques: true },
+ { techniqueID: 'T1218.001', score: 2 },
+ ]);
+ assert.equal(layer.gradient.maxValue, 2);
+});
+
+test('canonical source produces a deterministic API and one CSV row per command', async () => {
+ const catalog = await loadCatalog();
+ const rows = parseCsv(toCsv(catalog));
+ assert.ok(catalog.length > 200);
+ assert.equal(new Set(catalog.map((entry) => entry.url)).size, catalog.length);
+ assert.equal(
+ rows.length - 1,
+ catalog.reduce((sum, entry) => sum + entry.Commands.length, 0),
+ );
+ assert.ok(rows.every((row) => row.length === 17));
+ assert.deepEqual(catalog, await loadCatalog());
+ assert.ok(
+ catalog.some((entry) => entry.url === '/lolbas/Libraries/comsvcs/'),
+ );
+ assert.ok(
+ catalog.every((entry) => !entry.source.includes('HonorableMentions')),
+ );
+});
diff --git a/website/tests/preview.spec.ts b/website/tests/preview.spec.ts
new file mode 100644
index 000000000..c5a4e1bb4
--- /dev/null
+++ b/website/tests/preview.spec.ts
@@ -0,0 +1,62 @@
+import { test, expect } from '@playwright/test';
+
+test('LOL-CI-001 hosted fork preview keeps navigation, assets and data under its base path', async ({
+ page,
+ request,
+ context,
+}) => {
+ const base = process.env.SITE_BASE!;
+ const errors: string[] = [];
+ page.on('pageerror', (error) => errors.push(error.message));
+ page.on('console', (message) => {
+ if (message.type() === 'error') errors.push(message.text());
+ });
+ page.on('response', (response) => {
+ if (response.status() >= 400) errors.push(response.url());
+ });
+ await context.grantPermissions(['clipboard-read', 'clipboard-write']);
+ await page.goto(`${base}/`);
+ await expect(
+ page.getByRole('heading', {
+ name: 'Living Off The Land Binaries, Scripts and Libraries',
+ }),
+ ).toBeVisible();
+ expect(
+ await page
+ .locator('.brand-logo')
+ .evaluate((img) => (img as HTMLImageElement).naturalWidth),
+ ).toBeGreaterThan(0);
+ for (const path of await page
+ .locator('a[href^="/"], img[src^="/"], link[href^="/"]')
+ .evaluateAll((nodes) =>
+ nodes.map(
+ (node) => node.getAttribute('href') || node.getAttribute('src')!,
+ ),
+ ))
+ expect(path.startsWith(`${base}/`)).toBeTruthy();
+ await page.getByRole('searchbox').fill('certutil');
+ await expect(page.locator('[data-row]:visible')).toHaveCount(1);
+ await page.locator('.entry-name:visible').click();
+ await expect(page).toHaveURL(new RegExp(`${base}/lolbas/Binaries/Certutil/`));
+ const copy = page.locator('[data-copy]').first();
+ const command = await copy.getAttribute('data-copy');
+ await copy.click();
+ await expect(copy).toHaveText('Copied ✓');
+ expect(await page.evaluate(() => navigator.clipboard.readText())).toBe(
+ command,
+ );
+ await page.getByRole('link', { name: 'Insights', exact: true }).click();
+ await page.locator('.growth-chart a').last().click();
+ await expect(page).toHaveURL(new RegExp(`${base}/\\?year=`));
+ await expect(page.locator('#year-filter')).toContainText('Created in');
+ await page.getByRole('link', { name: 'API', exact: true }).click();
+ await page.locator('.endpoint').first().click();
+ await expect(page).toHaveURL(new RegExp(`${base}/api/lolbas.json`));
+ const response = await request.get(`${base}/api/lolbas.json`);
+ expect(response.ok()).toBeTruthy();
+ const data = await response.json();
+ expect(data.find((entry: any) => entry.Name === 'Certutil.exe').url).toBe(
+ 'https://lolbas-project.github.io/lolbas/Binaries/Certutil/',
+ );
+ expect(errors).toEqual([]);
+});
diff --git a/website/tests/release.test.mjs b/website/tests/release.test.mjs
new file mode 100644
index 000000000..70c2a8b4d
--- /dev/null
+++ b/website/tests/release.test.mjs
@@ -0,0 +1,242 @@
+import assert from 'node:assert/strict';
+import { readFileSync } from 'node:fs';
+import { test } from 'node:test';
+import { runInNewContext } from 'node:vm';
+import { parse } from 'yaml';
+
+const readWorkflow = (name) =>
+ parse(
+ readFileSync(
+ new URL(`../../.github/workflows/${name}`, import.meta.url),
+ 'utf8',
+ ),
+ );
+const site = readWorkflow('site.yml');
+const legacy = readWorkflow('gh-pages.yml');
+const canonical = 'LOLBAS-Project/LOLBAS';
+const publicationContext = (overrides = {}) => ({
+ vars: { ASTRO_PUBLISH_ENABLED: 'true' },
+ github: {
+ repository: canonical,
+ ref: 'refs/heads/master',
+ event_name: 'push',
+ event: {
+ repository: { fork: false },
+ workflow_run: {
+ conclusion: 'success',
+ event: 'push',
+ head_branch: 'master',
+ },
+ },
+ },
+ ...overrides,
+});
+// Conditions use the shared JavaScript/GitHub boolean-expression subset. Evaluate
+// the parsed workflow expression, so a weakened workflow changes these results.
+const enabled = (job, context) =>
+ Boolean(
+ runInNewContext(
+ job.if.replace(/^\s*\$\{\{([\s\S]*)\}\}\s*$/, '$1'),
+ context,
+ { timeout: 100 },
+ ),
+ );
+const action = (steps, name) =>
+ steps.find((step) => step.uses?.startsWith(`${name}@`));
+
+test('fork branch pushes build review artifacts while upstream feature pushes skip', () => {
+ assert.deepEqual(site.on.push.branches, ['**']);
+ assert.deepEqual(site.on.pull_request.branches, ['master']);
+ for (const repository of [canonical, 'contributor/LOLBAS']) {
+ for (const ref of ['refs/heads/master', 'refs/heads/feature']) {
+ for (const event_name of ['push', 'pull_request', 'workflow_dispatch']) {
+ const context = publicationContext();
+ Object.assign(context.github, { repository, ref, event_name });
+ assert.equal(
+ enabled(site.jobs.build, context),
+ event_name !== 'push' ||
+ repository !== canonical ||
+ ref === 'refs/heads/master',
+ );
+ if (repository !== canonical) {
+ assert.equal(enabled(site.jobs.deploy, context), false);
+ assert.equal(enabled(legacy.jobs.build, context), false);
+ }
+ }
+ }
+ }
+});
+
+test('only explicitly enabled canonical master push/manual runs may publish', () => {
+ for (const optIn of [undefined, '', 'false', 'TRUE', 'true']) {
+ for (const repository of [canonical, 'contributor/LOLBAS']) {
+ for (const ref of [
+ 'refs/heads/master',
+ 'refs/heads/feature',
+ 'refs/tags/master',
+ ]) {
+ for (const event_name of [
+ 'push',
+ 'pull_request',
+ 'pull_request_target',
+ 'workflow_run',
+ 'workflow_dispatch',
+ ]) {
+ const context = publicationContext();
+ context.vars.ASTRO_PUBLISH_ENABLED = optIn;
+ Object.assign(context.github, { repository, ref, event_name });
+ const allowed =
+ optIn === 'true' &&
+ repository === canonical &&
+ ref === 'refs/heads/master' &&
+ ['push', 'workflow_dispatch'].includes(event_name);
+ assert.equal(
+ enabled(site.jobs.deploy, context),
+ allowed,
+ JSON.stringify({ optIn, repository, ref, event_name }),
+ );
+ }
+ }
+ }
+ }
+});
+
+test('pull requests validate without exposing publication secrets or write permissions', () => {
+ assert.ok(site.on.pull_request);
+ assert.equal(site.on.pull_request_target, undefined);
+ assert.deepEqual(site.permissions, { contents: 'read' });
+ for (const [name, job] of Object.entries(site.jobs)) {
+ if (name === 'deploy') continue;
+ assert.doesNotMatch(
+ JSON.stringify(job),
+ /secrets\.|actions-gh-pages|deploy-pages/,
+ );
+ assert.ok(!Object.values(job.permissions ?? {}).includes('write'));
+ }
+ assert.equal(site.jobs.deploy.environment.name, 'github-pages');
+});
+
+test('publication depends on a successful fully validated build', () => {
+ assert.deepEqual([site.jobs.deploy.needs].flat(), ['build']);
+ assert.doesNotMatch(site.jobs.deploy.if, /always\(|failure\(|cancelled\(/);
+ assert.equal(site.jobs.build['continue-on-error'], undefined);
+ const steps = site.jobs.build.steps;
+ const required = [
+ /yamllint -c \.github\/\.yamllint yml\/\*\*\//,
+ /python(?:3)? \.github\/workflows\/validation\.py/,
+ /npm run check/,
+ /npm run test:data/,
+ /npm run build/,
+ /^npm test$/,
+ ];
+ const uploadIndex = steps.findIndex(
+ (step) => step.with?.path === 'website/dist/',
+ );
+ assert.ok(uploadIndex >= 0);
+ for (const command of required) {
+ const index = steps.findIndex((step) => command.test(step.run ?? ''));
+ assert.ok(
+ index >= 0 && index < uploadIndex,
+ `${command} must gate the artifact`,
+ );
+ assert.equal(steps[index]['continue-on-error'], undefined);
+ assert.equal(steps[index].if, undefined);
+ }
+ assert.equal(steps[uploadIndex].if, undefined);
+ assert.equal(steps[uploadIndex]['continue-on-error'], undefined);
+ for (const step of steps.filter((item) =>
+ /yamllint -c|validation\.py/.test(item.run ?? ''),
+ )) {
+ assert.equal(step['working-directory'], '.');
+ }
+});
+
+test('publisher downloads exactly the tested artifact from its own run without rebuilding', () => {
+ const upload = site.jobs.build.steps.find(
+ (step) => step.with?.path === 'website/dist/',
+ );
+ const download = action(site.jobs.deploy.steps, 'actions/download-artifact');
+ const publish = action(site.jobs.deploy.steps, 'peaceiris/actions-gh-pages');
+ assert.ok(upload?.uses.startsWith('actions/upload-artifact@'));
+ assert.equal(download.with.name, upload.with.name);
+ assert.match(download.with.name, /github\.sha/);
+ assert.match(download.with.name, /github\.run_attempt/);
+ assert.equal(download.with['run-id'], undefined);
+ assert.equal(download.with.repository, undefined);
+ assert.equal(publish.with.publish_dir, download.with.path);
+ assert.equal(
+ publish.with.external_repository,
+ 'LOLBAS-Project/LOLBAS-Project.github.io',
+ );
+ assert.equal(publish.with.publish_branch, 'master');
+ assert.equal(publish.with.destination_dir, undefined);
+ assert.equal(publish.with.keep_files, false);
+ assert.equal(publish.with.enable_jekyll, false);
+ assert.match(publish.with.deploy_key, /secrets\.ACTIONS_DEPLOY_KEY/);
+ assert.ok(
+ site.jobs.deploy.steps.some(
+ (step) =>
+ /source-revision\.txt/.test(step.run ?? '') &&
+ /GITHUB_SHA/.test(step.run),
+ ),
+ );
+ assert.doesNotMatch(
+ JSON.stringify(site.jobs.deploy.steps),
+ /actions\/checkout|npm (?:ci|run build)/,
+ );
+});
+
+test('legacy publisher is mutually exclusive and accepts only validated upstream pushes', () => {
+ for (const optIn of [undefined, '', 'false', 'true']) {
+ for (const conclusion of ['success', 'failure', 'cancelled', 'skipped']) {
+ for (const event of ['push', 'pull_request', 'workflow_dispatch']) {
+ for (const head_branch of ['master', 'feature']) {
+ for (const fork of [false, true]) {
+ for (const repository of [canonical, 'contributor/LOLBAS']) {
+ const context = publicationContext();
+ context.vars.ASTRO_PUBLISH_ENABLED = optIn;
+ context.github.repository = repository;
+ context.github.event.repository.fork = fork;
+ context.github.event.workflow_run = {
+ conclusion,
+ event,
+ head_branch,
+ };
+ const allowed =
+ optIn !== 'true' &&
+ conclusion === 'success' &&
+ event === 'push' &&
+ head_branch === 'master' &&
+ !fork &&
+ repository === canonical;
+ assert.equal(
+ enabled(legacy.jobs.build, context),
+ allowed,
+ JSON.stringify({
+ optIn,
+ conclusion,
+ event,
+ head_branch,
+ fork,
+ repository,
+ }),
+ );
+ assert.ok(
+ !(
+ enabled(legacy.jobs.build, context) &&
+ enabled(site.jobs.deploy, context)
+ ),
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+ const checkout = action(legacy.jobs.build.steps, 'actions/checkout');
+ assert.equal(checkout.with.ref, '${{ github.event.workflow_run.head_sha }}');
+ assert.equal(
+ legacy.jobs.build.concurrency.group,
+ site.jobs.deploy.concurrency.group,
+ );
+});
diff --git a/website/tests/site.spec.ts b/website/tests/site.spec.ts
new file mode 100644
index 000000000..6a78d6e23
--- /dev/null
+++ b/website/tests/site.spec.ts
@@ -0,0 +1,386 @@
+import { test, expect } from '@playwright/test';
+import { loadCatalog } from '../scripts/prepare-data.mjs';
+import type { CatalogEntry } from '../src/lib/catalog';
+const source = (await loadCatalog()) as CatalogEntry[];
+const techniqueCount = new Set(
+ source.flatMap((e) => e.Commands.map((c) => c.MitreID)),
+).size;
+
+test('LOL-SITE-002 search, legacy syntax, sorting, pagination and URL state', async ({
+ page,
+}) => {
+ await page.goto('/');
+ const rows = page.locator('[data-row]:visible');
+ await expect(rows).toHaveCount(25);
+ await page.getByRole('button', { name: 'Next →' }).click();
+ await expect(page.locator('#page-info')).toContainText('Page 2');
+ await page.reload();
+ await expect(page.locator('#page-info')).toContainText('Page 2');
+ await page.getByRole('searchbox').fill('certutil');
+ await expect(rows).toHaveCount(1);
+ await expect(rows.first()).toContainText('Certutil.exe');
+ await page.locator('#category').selectOption('Download');
+ await expect(rows).toHaveCount(1);
+ await page.reload();
+ await expect(page.getByRole('searchbox')).toHaveValue('certutil');
+ await expect(page.locator('#category')).toHaveValue('Download');
+ await expect(rows).toHaveCount(1);
+ await page.getByRole('button', { name: 'Reset' }).click();
+ await page.getByRole('searchbox').fill('/download certutil');
+ await expect(rows).toHaveCount(1);
+ await expect(rows.first()).toContainText('Certutil.exe');
+ await page.getByRole('searchbox').fill('/uac bypass');
+ await expect(rows).not.toHaveCount(0);
+ await expect(rows.first()).toContainText('UAC Bypass');
+ await page.getByRole('searchbox').fill('#Script');
+ await expect(rows).not.toHaveCount(0);
+ for (const row of await rows.all())
+ await expect(row).toHaveAttribute('data-type', 'Scripts');
+ await page.getByRole('button', { name: 'Reset' }).click();
+ await page.locator('#sort').selectOption('newest');
+ const dates = await rows.evaluateAll((rs) =>
+ rs.map((r) => (r as HTMLElement).dataset.created!),
+ );
+ expect(dates).toEqual([...dates].sort().reverse());
+ await page.locator('#sort').selectOption('commands');
+ const counts = await rows.evaluateAll((rs) =>
+ rs.map((r) => Number((r as HTMLElement).dataset.commands)),
+ );
+ expect(counts).toEqual([...counts].sort((a, b) => b - a));
+ await page.getByRole('searchbox').fill('no-such-lolbas-zzzz');
+ await expect(page.locator('#empty-state')).toBeVisible();
+ await page.getByRole('button', { name: 'Clear all filters' }).click();
+ await expect(rows).toHaveCount(25);
+ await page.getByRole('searchbox').fill('certutil');
+ await page.locator('.entry-name:visible').click();
+ await expect(
+ page.getByRole('heading', { name: 'Certutil.exe', exact: true }),
+ ).toBeVisible();
+});
+
+test('LOL-SITE-002 combined technique/type/detection/date filters and chart deep links', async ({
+ page,
+}) => {
+ await page.goto(
+ '/?technique=T1105&type=Binaries&detection=yes&year=2018#catalog',
+ );
+ const rows = page.locator('[data-row]:visible');
+ expect(await rows.count()).toBeGreaterThan(0);
+ for (const row of await rows.all()) {
+ await expect(row).toHaveAttribute('data-type', 'Binaries');
+ await expect(row).toHaveAttribute('data-detection', 'yes');
+ await expect(row).toHaveAttribute('data-created', /^2018/);
+ await expect(row).toHaveAttribute('data-techniques', /T1105/);
+ }
+ await page.goto('/insights/');
+ await page.locator('.growth-chart a').last().click();
+ await expect(page.locator('#year-filter')).toContainText('Created in');
+});
+
+test('LOL-SITE-003 legacy detail anchor, content and copy command', async ({
+ page,
+ context,
+}) => {
+ await context.grantPermissions(['clipboard-read', 'clipboard-write']);
+ await page.goto('/lolbas/Binaries/Certutil/#download');
+ await expect(
+ page.getByRole('heading', { name: 'Certutil.exe', exact: true }),
+ ).toBeVisible();
+ await expect(page.locator('[id="download"]')).toBeVisible();
+ await expect(page.locator('main')).toContainText(
+ 'C:\\Windows\\System32\\certutil.exe',
+ );
+ await expect(page.locator('main')).toContainText('Microsoft-CryptoAPI/10.0');
+ const button = page.locator('[data-copy]').first();
+ const value = await button.getAttribute('data-copy');
+ await button.click();
+ await expect(button).toHaveText('Copied ✓');
+ expect(await page.evaluate(() => navigator.clipboard.readText())).toBe(value);
+ await expect(
+ page.locator('a[href*="yml/OSBinaries/Certutil.yml"]').first(),
+ ).toBeVisible();
+});
+
+test('LOL-API-001 production API and Navigator retain legacy contracts', async ({
+ request,
+}) => {
+ const data = await (await request.get('/api/lolbas.json')).json();
+ expect(data).toHaveLength(source.length);
+ expect(Object.keys(data[0])).toEqual([
+ 'Name',
+ 'Description',
+ 'Author',
+ 'Created',
+ 'Commands',
+ 'Full_Path',
+ 'Detection',
+ 'Resources',
+ 'url',
+ ]);
+ const cert = data.find((e: any) => e.Name === 'Certutil.exe');
+ expect(cert.url).toBe(
+ 'https://lolbas-project.github.io/lolbas/Binaries/Certutil/',
+ );
+ expect(cert.Commands).toEqual(
+ source.find((e) => e.Name === 'Certutil.exe')!.Commands,
+ );
+ const csv = await (await request.get('/api/lolbas.csv')).text();
+ expect(csv.split('\n')[0].trim()).toBe(
+ 'Filename,Description,Author,Date,Command,Command Description,Command Usecase,Command Category,Command Privileges,MITRE ATT&CK technique,Operating System,Paths,Detections,Resources,Acknowledgements,URL,Tags',
+ );
+ const nav = await (
+ await request.get('/mitre_attack_navigator_layer.json')
+ ).json();
+ expect(nav.domain).toBe('enterprise-attack');
+ expect(nav.techniques.length).toBe(techniqueCount);
+ expect((await request.get('/assets/lolbas-count.svg')).ok()).toBeTruthy();
+});
+
+test('LOL-SITE-001 no-JavaScript catalog and detail navigation', async ({
+ browser,
+}) => {
+ const context = await browser.newContext({ javaScriptEnabled: false });
+ const page = await context.newPage();
+ await page.goto('http://127.0.0.1:4388/');
+ await expect(page.locator('[data-row]')).toHaveCount(source.length);
+ await page
+ .locator('.entry-name')
+ .filter({ hasText: 'AddinUtil.exe' })
+ .click();
+ await expect(
+ page.getByRole('heading', { name: 'AddinUtil.exe', exact: true }),
+ ).toBeVisible();
+ await context.close();
+});
+
+test('LOL-SITE-001 mobile layout, keyboard access and clean runtime', async ({
+ page,
+}) => {
+ const errors: string[] = [];
+ page.on('pageerror', (e) => errors.push(e.message));
+ page.on('console', (msg) => {
+ if (msg.type() === 'error') errors.push(msg.text());
+ });
+ page.on('response', (res) => {
+ if (res.status() >= 400) errors.push(`${res.status()} ${res.url()}`);
+ });
+ await page.setViewportSize({ width: 390, height: 844 });
+ await page.goto('/');
+ expect(
+ await page.evaluate(
+ () => document.documentElement.scrollWidth <= innerWidth,
+ ),
+ ).toBeTruthy();
+ await page.keyboard.press('/');
+ await expect(page.getByRole('searchbox')).toBeFocused();
+ await page.getByRole('searchbox').fill('AddinUtil');
+ await page.locator('.entry-name:visible').click();
+ expect(
+ await page.evaluate(
+ () => document.documentElement.scrollWidth <= innerWidth,
+ ),
+ ).toBeTruthy();
+ expect(errors).toEqual([]);
+});
+
+test('LOL-SITE-001 about button stays readable before hover and maintainers are linked first', async ({
+ page,
+}) => {
+ await page.goto('/about/');
+ const button = page.getByRole('link', { name: 'Explore the repository' });
+ await button.scrollIntoViewIfNeeded();
+ const contrast = () =>
+ button.evaluate((node) => {
+ const style = getComputedStyle(node);
+ const luminance = (color: string) => {
+ const channels = color
+ .match(/[\d.]+/g)!
+ .slice(0, 3)
+ .map(Number)
+ .map((n) => {
+ const channel = n / 255;
+ return channel <= 0.04045
+ ? channel / 12.92
+ : ((channel + 0.055) / 1.055) ** 2.4;
+ });
+ return (
+ channels[0] * 0.2126 + channels[1] * 0.7152 + channels[2] * 0.0722
+ );
+ };
+ const foreground = luminance(style.color),
+ background = luminance(style.backgroundColor);
+ return (
+ (Math.max(foreground, background) + 0.05) /
+ (Math.min(foreground, background) + 0.05)
+ );
+ });
+ expect(await contrast()).toBeGreaterThanOrEqual(4.5);
+ await button.hover();
+ expect(await contrast()).toBeGreaterThanOrEqual(4.5);
+ await expect(page.locator('.article h2').first()).toHaveText(
+ 'Project maintainers',
+ );
+ await expect(page.locator('.maintainer-list a')).toHaveCount(7);
+ for (const handle of [
+ 'oddvarmoe',
+ 'bohops',
+ 'xenosCR',
+ 'ConsciousHacker',
+ 'liamsomerville',
+ 'Wietze',
+ '_josehelps',
+ ]) {
+ await expect(
+ page.getByRole('link', { name: new RegExp(`@${handle}`) }),
+ ).toHaveAttribute('href', `https://x.com/${handle}`);
+ }
+ await page.setViewportSize({ width: 390, height: 844 });
+ expect(
+ await page.evaluate(
+ () => document.documentElement.scrollWidth <= innerWidth,
+ ),
+ ).toBeTruthy();
+});
+
+test('LOL-SITE-001 visual evidence for catalog, detail and mobile', async ({
+ page,
+}, testInfo) => {
+ const { mkdir, copyFile } = await import('node:fs/promises');
+ const { resolve } = await import('node:path');
+ async function capture(name: string) {
+ const path = testInfo.outputPath(`${name}.png`);
+ await page.screenshot({ path, fullPage: false });
+ await testInfo.attach(name, { path, contentType: 'image/png' });
+ if (!process.env.CI) {
+ await mkdir(resolve('../.context'), { recursive: true });
+ await copyFile(path, resolve(`../.context/${name}.png`));
+ }
+ }
+ await page.setViewportSize({ width: 1440, height: 1100 });
+ await page.goto('/');
+ for (const width of [1440, 900]) {
+ await page.setViewportSize({ width, height: 1100 });
+ const terminal = await page.locator('.terminal').boundingBox();
+ const label = await page.locator('.label-bottom').boundingBox();
+ expect(terminal).not.toBeNull();
+ expect(label).not.toBeNull();
+ expect(terminal!.y + terminal!.height + 8).toBeLessThanOrEqual(label!.y);
+ }
+ await page.setViewportSize({ width: 1440, height: 1100 });
+ await capture('lolbas-home');
+ await page.locator('#catalog').scrollIntoViewIfNeeded();
+ await capture('lolbas-catalog');
+ await page.goto('/lolbas/Binaries/Addinutil/');
+ await expect(
+ page.getByRole('heading', { name: 'AddinUtil.exe', exact: true }),
+ ).toBeVisible();
+ await expect.poll(() => page.evaluate(() => scrollY)).toBe(0);
+ await capture('lolbas-detail');
+ await page.goto('/about/');
+ await expect.poll(() => page.evaluate(() => scrollY)).toBe(0);
+ await capture('lolbas-about');
+ await page
+ .getByRole('link', { name: 'Explore the repository' })
+ .scrollIntoViewIfNeeded();
+ await capture('lolbas-about-button');
+ await page.setViewportSize({ width: 390, height: 844 });
+ await page.goto('/');
+ await capture('lolbas-mobile');
+ await page.goto('/about/');
+ await expect.poll(() => page.evaluate(() => scrollY)).toBe(0);
+ await capture('lolbas-about-mobile');
+});
+
+test('LOL-SITE-002 styled filter menu supports pointer, keyboard and dismissal', async ({
+ page,
+}, testInfo) => {
+ await page.setViewportSize({ width: 1440, height: 900 });
+ await page.goto('/?type=Binaries#catalog');
+ const category = page.locator('#category');
+ await category.click();
+ const download = page.getByRole('option', { name: 'Download', exact: true });
+ await expect(download).toBeVisible();
+ const path = testInfo.outputPath('lolbas-dropdown.png');
+ await page.screenshot({ path });
+ await testInfo.attach('Styled filter menu', {
+ path,
+ contentType: 'image/png',
+ });
+ if (!process.env.CI) {
+ const { copyFile, mkdir } = await import('node:fs/promises');
+ const { resolve } = await import('node:path');
+ await mkdir(resolve('../.context'), { recursive: true });
+ await copyFile(path, resolve('../.context/lolbas-dropdown.png'));
+ }
+ await download.click();
+ await expect(category).toHaveValue('Download');
+ const rows = page.locator('[data-row]:visible');
+ expect(await rows.count()).toBeGreaterThan(0);
+ for (const row of await rows.all())
+ await expect(row).toHaveAttribute('data-categories', /Download/);
+ await page.locator('#sort').focus();
+ await page.keyboard.press('Space');
+ await page.keyboard.press('ArrowDown');
+ await page.keyboard.press('Enter');
+ await expect(page.locator('#sort')).toHaveValue('newest');
+ await category.click();
+ await expect(download).toBeVisible();
+ await page.keyboard.press('Escape');
+ await expect(category).toHaveValue('Download');
+ await expect(category).toBeFocused();
+});
+
+test('LOL-SITE-002 column sorting toggles direction, persists and follows filter/reset state', async ({
+ page,
+}) => {
+ await page.goto('/?category=Execute');
+ const rows = page.locator('[data-row]:visible');
+ const cases = [
+ { key: 'name', attr: 'data-name', numeric: false },
+ { key: 'functions', attr: 'data-functions', numeric: false },
+ { key: 'type', attr: 'data-type-label', numeric: false },
+ { key: 'attack', attr: 'data-attack', numeric: false },
+ { key: 'detections', attr: 'data-detections', numeric: true },
+ ];
+ for (const { key, attr, numeric } of cases) {
+ const button = page.locator(`[data-sort="${key}"]`);
+ let previousDirection: string | null = null;
+ for (let click = 0; click < 2; click++) {
+ await page.getByRole('button', { name: 'Next →' }).click();
+ await expect(page.locator('#page-info')).toContainText('Page 2');
+ await button.click();
+ await expect(page.locator('#page-info')).toContainText('Page 1');
+ const direction = await button.locator('..').getAttribute('aria-sort');
+ expect(['ascending', 'descending']).toContain(direction);
+ if (previousDirection) expect(direction).not.toBe(previousDirection);
+ previousDirection = direction;
+ const values = await rows.evaluateAll(
+ (elements, attribute) =>
+ elements.map((el) => el.getAttribute(attribute)!),
+ attr,
+ );
+ const expected = [...values].sort((a, b) =>
+ numeric
+ ? Number(a) - Number(b)
+ : a.localeCompare(b, 'en', { numeric: key === 'attack' }),
+ );
+ if (direction === 'descending') expected.reverse();
+ expect(values).toEqual(expected);
+ const sort = await page.locator('#sort').inputValue();
+ await page.reload();
+ await expect(page.locator('#sort')).toHaveValue(sort);
+ await expect(button.locator('..')).toHaveAttribute(
+ 'aria-sort',
+ direction!,
+ );
+ await expect(page.locator('#category')).toHaveValue('Execute');
+ }
+ }
+ await page.getByRole('button', { name: 'Reset' }).click();
+ await expect(
+ page.locator('[data-sort="name"]').locator('..'),
+ ).toHaveAttribute('aria-sort', 'ascending');
+ await page.locator('[data-sort="name"]').focus();
+ await page.keyboard.press('Enter');
+ await expect(page.locator('#sort')).toHaveValue('reverse');
+});
diff --git a/website/tsconfig.json b/website/tsconfig.json
new file mode 100644
index 000000000..a20d33fb5
--- /dev/null
+++ b/website/tsconfig.json
@@ -0,0 +1,6 @@
+{
+ "extends": "astro/tsconfigs/strict",
+ "compilerOptions": { "resolveJsonModule": true },
+ "include": [".astro/types.d.ts", "src/**/*", "tests/**/*.ts", "*.ts"],
+ "exclude": ["dist", "node_modules", "playwright-report", "test-results"]
+}