From 342d0f727135692afbfe3ceb73f8c0b7988d5387 Mon Sep 17 00:00:00 2001
From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com>
Date: Thu, 22 Oct 2026 15:41:39 +0200
Subject: [PATCH] feat: write the site for Starlight behind a flag
generate_site.py --engine starlight writes the pages, navigation and
data files of an Astro Starlight project in website/. Links resolve to
site addresses, every heading keeps its MkDocs id, and pages split by
size are measured in the MkDocs Markdown so both builds cut the same
files. The 839 pages and their 45,613 ids match the MkDocs build.
---
.github/workflows/deploy-site.yml | 2 +
scripts/generate_site.py | 250 +-
scripts/sitemarkup.py | 14 +
scripts/siterender.py | 4 +-
scripts/sitestarlight.py | 572 ++
website/.gitignore | 7 +
website/astro.config.mjs | 85 +
website/package-lock.json | 5403 ++++++++++++++++++
website/package.json | 17 +
website/src/assets/logo-dark.svg | 1 +
website/src/assets/logo-light.svg | 1 +
website/src/components/FilePicker.astro | 186 +
website/src/components/Footer.astro | 10 +
website/src/components/MarkdownContent.astro | 21 +
website/src/components/PageTitle.astro | 40 +
website/src/content.config.ts | 22 +
website/src/env.d.ts | 7 +
website/src/routeData.ts | 96 +
website/src/scripts/site.ts | 275 +
website/src/styles/retrobios.css | 557 ++
website/tools/axe.mjs | 24 +
website/tools/check_tools.mjs | 44 +
website/tools/lighthouse.py | 57 +
website/tools/measure.mjs | 106 +
website/tools/serve.mjs | 51 +
website/tools/shots.mjs | 25 +
26 files changed, 7817 insertions(+), 60 deletions(-)
create mode 100644 scripts/sitestarlight.py
create mode 100644 website/.gitignore
create mode 100644 website/astro.config.mjs
create mode 100644 website/package-lock.json
create mode 100644 website/package.json
create mode 100644 website/src/assets/logo-dark.svg
create mode 100644 website/src/assets/logo-light.svg
create mode 100644 website/src/components/FilePicker.astro
create mode 100644 website/src/components/Footer.astro
create mode 100644 website/src/components/MarkdownContent.astro
create mode 100644 website/src/components/PageTitle.astro
create mode 100644 website/src/content.config.ts
create mode 100644 website/src/env.d.ts
create mode 100644 website/src/routeData.ts
create mode 100644 website/src/scripts/site.ts
create mode 100644 website/src/styles/retrobios.css
create mode 100644 website/tools/axe.mjs
create mode 100644 website/tools/check_tools.mjs
create mode 100644 website/tools/lighthouse.py
create mode 100644 website/tools/measure.mjs
create mode 100644 website/tools/serve.mjs
create mode 100644 website/tools/shots.mjs
diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml
index 22c6dd54..27ca1287 100644
--- a/.github/workflows/deploy-site.yml
+++ b/.github/workflows/deploy-site.yml
@@ -45,9 +45,11 @@ on:
- "scripts/sect233r1.py"
- "scripts/sitediagrams.py"
- "scripts/siteguides.py"
+ - "scripts/sitemarkup.py"
- "scripts/siteexports.py"
- "scripts/siterender.py"
- "scripts/sitesources.py"
+ - "scripts/sitestarlight.py"
- "scripts/sitewrite.py"
- "scripts/slot.py"
- "scripts/slots.py"
diff --git a/scripts/generate_site.py b/scripts/generate_site.py
index f8e6da82..bd41ec6c 100644
--- a/scripts/generate_site.py
+++ b/scripts/generate_site.py
@@ -74,6 +74,7 @@ from generate_readme import (
)
from provenance_report import build_report, distinct_missing
import sitemarkup as markup
+import sitestarlight
from siteexports import generate_exports_page, load_export_reports
from siteguides import (
ALSO_RUNS,
@@ -91,11 +92,16 @@ from siteguides import (
import release_record
DOCS_DIR = "docs"
+# The Starlight project, and where its pages go inside it.
+STARLIGHT_DIR = "website"
+STARLIGHT_CONTENT = "src/content/docs"
SITE_NAME = "RetroBIOS"
REPO_URL = "https://github.com/Abdess/retrobios"
RELEASE_URL = f"{REPO_URL}/releases/latest"
SITE_URL = "https://abdess.github.io/retrobios/"
GENERATED_DIRS = ["platforms", "systems", "emulators", "wiki", "api", "downloads"]
+# Generated directories of files served as they are, not pages.
+PUBLIC_DIRS = ("api", "downloads")
@@ -1873,8 +1879,15 @@ def _front_matter(markdown: str) -> dict:
return data if isinstance(data, dict) else {}
-def decorate_markdown_pages(docs: Path) -> None:
- """Add per-page descriptions and machine-readable structured data."""
+def decorate_markdown_pages(docs: Path) -> dict[str, str]:
+ """Add per-page descriptions and machine-readable structured data.
+
+ Under Starlight each page is written in its final form (front matter,
+ site addresses, pinned heading ids) and the dates a page states are
+ returned by address, for the sitemap.
+ """
+ lastmod: dict[str, str] = {}
+ terms = sitestarlight.glossary_terms(_REPO_ROOT / WIKI_SRC_DIR / "glossary.md")
for path in sorted(docs.rglob("*.md")):
relative = path.relative_to(docs)
if any(part in LOCAL_NOTE_DIRS for part in relative.parts[:-1]):
@@ -1918,6 +1931,23 @@ def decorate_markdown_pages(docs: Path) -> None:
},
**PAGE_DATA.get(relative.as_posix(), {}),
}
+ if markup.starlight():
+ target, text = sitestarlight.finalize_page(
+ relative.as_posix(), markdown, level=level,
+ boost=SEARCH_BOOST.get(level), browser_title=browser_title,
+ description=description, structured=structured, terms=terms,
+ search=authored.get("search") != "exclude",
+ )
+ write_decorated(docs / target, text)
+ # One file per page: a page that gained or lost its components
+ # changed extension, and the other one would claim its address.
+ stem = relative.with_suffix("").as_posix()
+ for suffix in (".md", ".mdx"):
+ if f"{stem}{suffix}" != target:
+ (docs / f"{stem}{suffix}").unlink(missing_ok=True)
+ if structured.get("dateModified"):
+ lastmod[sitestarlight.page_url(target)] = str(structured["dateModified"])
+ continue
structured_json = json.dumps(
structured, ensure_ascii=False, separators=(",", ":")
).replace("", "<\\/")
@@ -1937,6 +1967,7 @@ def decorate_markdown_pages(docs: Path) -> None:
+ (f"{level_label(level)}\n\n" if level else "")
)
write_decorated(path, front_matter + markdown)
+ return lastmod
# Platform pages
@@ -2408,7 +2439,7 @@ def generate_platform_page(
logo_url = registry.get(name, {}).get("logo", "")
logo_md = (
- f"{{ width=48 align=right }}\n\n" if logo_url else ""
+ markup.image(display, logo_url, width="48", align="right") + "\n\n" if logo_url else ""
)
homepage = config.get("homepage", "")
@@ -3467,12 +3498,13 @@ def _cards_markdown(cards: list[Card]) -> list[str]:
return lines
-def _parts(cards: list[Card], budget: int) -> list[list[Card]]:
- """Cards in runs of at most ``budget`` bytes of Markdown, in order."""
+def _parts(cards: list[Card], budget: int, sizes: list[int] | None = None) -> list[list[Card]]:
+ """Cards in runs of at most ``budget`` bytes of Markdown, in order.
+ ``sizes`` gives each card's weight when it is not its own Markdown."""
parts: list[list[Card]] = [[]]
size = 0
- for card in cards:
- weight = sum(len(line) + 1 for line in card[1])
+ for index, card in enumerate(cards):
+ weight = sizes[index] if sizes else sum(len(line) + 1 for line in card[1])
if parts[-1] and size + weight > budget:
parts.append([])
size = 0
@@ -3507,11 +3539,11 @@ def system_pages(
"""
slug = _slug(manufacturer)
json_link = (f"[JSON](../api/v1/systems/{urllib.parse.quote(slug)}.json)")
- whole_anchors: set[str] = set()
- sections = {
- console: _console_cards(manufacturer, consoles[console], consumers, whole_anchors)
- for console in sorted(consoles)
- }
+ def all_sections() -> dict[str, list[Card]]:
+ anchors: set[str] = set()
+ return {console: _console_cards(manufacturer, consoles[console], consumers, anchors)
+ for console in sorted(consoles)}
+
intro = (
f"Every BIOS file the collection holds for {manufacturer} machines, one "
"card per file. A card gives the file's fingerprints, the emulators that "
@@ -3520,29 +3552,40 @@ def system_pages(
"first, the others under \"Variants\". To install the files of one "
"console, open its \"Install\" box."
)
- lines = [
- f"# {manufacturer}", "", intro, "",
- f"The files below by console, with every hash, are also published as {json_link}.",
- "",
- ]
- for console, cards in sections.items():
- lines.append(f"## {system_icon_markdown(manufacturer, console)}{console}")
- lines.append("")
- lines.extend(_console_install_block(console, (installs or {}).get(console, [])))
- lines.extend(_cards_markdown(cards))
- lines.append("")
- lines.extend(_next_block(_NEXT_FROM_SYSTEMS))
- lines.append(f'
Generated on {_timestamp()}.
')
- whole = "\n".join(lines) + "\n"
- if len(whole) <= budget:
- return {slug: (whole, consoles)}
+ def whole_page(sections: dict[str, list[Card]]) -> str:
+ lines = [
+ f"# {manufacturer}", "", intro, "",
+ f"The files below by console, with every hash, are also published as {json_link}.",
+ "",
+ ]
+ for console, cards in sections.items():
+ lines.append(f"## {system_icon_markdown(manufacturer, console)}{console}")
+ lines.append("")
+ lines.extend(_console_install_block(console, (installs or {}).get(console, [])))
+ lines.extend(_cards_markdown(cards))
+ lines.append("")
+ lines.extend(_next_block(_NEXT_FROM_SYSTEMS))
+ lines.append(f'Generated on {_timestamp()}.
')
+ return "\n".join(lines) + "\n"
+
+ def card_weight(card: list[str]) -> int:
+ return sum(len(line) + 1 for line in card)
+
+ # Sizes are those of the MkDocs Markdown the budget was tuned on, so
+ # every engine splits the same pages at the same cards.
+ with markup.using("mkdocs"):
+ measured = all_sections()
+ measured_text = whole_page(measured)
+ if len(measured_text) <= budget:
+ text = whole_page(all_sections()) if markup.starlight() else measured_text
+ return {slug: (text, consoles)}
# The heaviest sections move to pages of their own until what stays on
# the manufacturer's page is half the budget: a section of one file
# keeps its place, and a reader does not open 91 pages to browse Other.
weights = {
- console: sum(len(line) + 1 for _variant, card, _sha1 in cards for line in card)
- for console, cards in sections.items()
+ console: sum(card_weight(card) for _variant, card, _sha1 in cards)
+ for console, cards in measured.items()
}
moved: set[str] = set()
for console in sorted(weights, key=lambda c: (-weights[c], c)):
@@ -3570,7 +3613,10 @@ def system_pages(
# Cards are rendered again for their own page: the anchors a page
# holds are its own.
cards = _console_cards(manufacturer, consoles[console], consumers, set())
- parts = _parts(cards, budget)
+ with markup.using("mkdocs"):
+ sizes = [card_weight(card) for _variant, card, _sha1
+ in _console_cards(manufacturer, consoles[console], consumers, set())]
+ parts = _parts(cards, budget, sizes)
base = f"{slug}-{_slugify_anchor(console) or 'files'}"
stems = [base if index == 0 else f"{base}-{index + 1}" for index in range(len(parts))]
record_console_pages(slug, console, stems, [
@@ -3616,7 +3662,11 @@ def system_pages(
def _part_range(part: list[Card]) -> str:
"""The first and last file names of a part, as the cards show them."""
- names = [re.search(r"`([^`]*)`", card[2]).group(1) for _variant, card, _sha1 in part]
+ names = []
+ for _variant, card, _sha1 in part:
+ # A Markdown heading under MkDocs, an HTML one under Starlight.
+ match = re.search(r"`([^`]*)`|(.*?)", card[2])
+ names.append(match[1] if match[1] is not None else html.unescape(match[2]))
if len(names) == 1:
return f"`{names[0]}`"
return f"{_count(len(names), 'file')}, `{names[0]}` to `{names[-1]}`"
@@ -5852,6 +5902,31 @@ def generate_contributing() -> str:
# The end of a reference page: back to the index, or on to contributing.
+def generate_not_found() -> str:
+ """The page a stale link lands on. Pages have moved before (file cards
+ refiled between manufacturers), so it offers the search, the two pages
+ a visitor most often wants, and the list a moved page belongs to."""
+ return "\n".join([
+ "---",
+ "search: exclude",
+ "---",
+ "",
+ "# Page not found",
+ "",
+ 'This address has no page. It may have moved since the link was written.
',
+ "",
+ 'Search the site
',
+ "",
+ "- [Start here](index.md): what BIOS files are and how to install them",
+ "- [Get your files](which-pack.md): the pack or the command for your device",
+ "- Browse [platforms](platforms/index.md), [systems](systems/index.md) "
+ "or [emulators](emulators/index.md)",
+ "",
+ '
',
+ "",
+ ])
+
+
def reference_next() -> str:
return "\n" + "\n".join(markup.aside("tip", "Next", [
"- [Reference index](wiki/index.md), every reference page in one list",
@@ -6248,6 +6323,13 @@ def generate_which_pack() -> str:
"",
"Run the command and press **c** when it asks.",
"",
+ *([
+ "Or choose your setup and consoles here. You get the list of files "
+ "and a command that installs only those.",
+ "",
+ " ",
+ "",
+ ] if markup.starlight() else []),
*_next_block([
"[First install, step by step](wiki/first-install.md)",
"[When a game does not start](wiki/troubleshooting.md)",
@@ -6467,10 +6549,23 @@ def main() -> None:
help="emulator profile directory (default: %(default)s)",
)
parser.add_argument(
- "--docs-dir", default=DOCS_DIR,
- help="directory the site sources are written to (default: %(default)s)",
+ "--engine", choices=markup.ENGINES, default="mkdocs",
+ help="site engine the pages are written for (default: %(default)s)",
+ )
+ parser.add_argument(
+ "--site-dir", default=STARLIGHT_DIR,
+ help="the Starlight project, with --engine starlight (default: %(default)s)",
+ )
+ parser.add_argument(
+ "--docs-dir", default=None,
+ help="directory the pages are written to (default: docs, or the "
+ "Starlight project's content collection)",
)
args = parser.parse_args()
+ markup.set_engine(args.engine)
+ if args.docs_dir is None:
+ args.docs_dir = (str(Path(args.site_dir) / STARLIGHT_CONTENT)
+ if markup.starlight() else DOCS_DIR)
try:
# Two runs over one docs/ delete each other's pages and scratch
# files in the sweep, so the second is refused instead.
@@ -6492,34 +6587,42 @@ def _generate(args: argparse.Namespace) -> None:
db = load_database(args.db)
docs = Path(args.docs_dir)
+ # Files the site serves as they are (data, downloads, images): beside the
+ # pages under MkDocs, in the project's public folder under Starlight.
+ public = Path(args.site_dir) / "public" if markup.starlight() else docs
for d in GENERATED_DIRS:
- (docs / d).mkdir(parents=True, exist_ok=True)
+ (public / d if d in PUBLIC_DIRS else docs / d).mkdir(parents=True, exist_ok=True)
- css_src = Path("docs_assets") / "extra.css"
- css_dest = docs / "stylesheets" / "extra.css"
- if css_src.exists():
- css_dest.parent.mkdir(parents=True, exist_ok=True)
- shutil.copy2(css_src, css_dest)
- js_src = Path("docs_assets") / "site.js"
- js_dest = docs / "javascripts" / "site.js"
- if js_src.exists():
- js_dest.parent.mkdir(parents=True, exist_ok=True)
- shutil.copy2(js_src, js_dest)
+ if not markup.starlight():
+ css_src = Path("docs_assets") / "extra.css"
+ css_dest = docs / "stylesheets" / "extra.css"
+ if css_src.exists():
+ css_dest.parent.mkdir(parents=True, exist_ok=True)
+ shutil.copy2(css_src, css_dest)
+ js_src = Path("docs_assets") / "site.js"
+ js_dest = docs / "javascripts" / "site.js"
+ if js_src.exists():
+ js_dest.parent.mkdir(parents=True, exist_ok=True)
+ shutil.copy2(js_src, js_dest)
# No robots.txt: a crawler reads it only at the host root (RFC 9309), and
# this project site lives under /retrobios/. Every page's head points at
# the sitemap instead (overrides/main.html).
- (docs / "robots.txt").unlink(missing_ok=True)
+ (public / "robots.txt").unlink(missing_ok=True)
- images_dest = docs / "assets" / "images"
+ images_dest = public / "assets" / "images"
images_dest.mkdir(parents=True, exist_ok=True)
# Logos, system icons and sharing cards are served from the site itself
# (docs_assets/*/sources.json records where each came from).
- for folder in ("logos", "system-icons", "og"):
+ folders = [("logos", "logos"), ("system-icons", "system-icons"), ("og", "og")]
+ if markup.starlight():
+ # The theme's own folder under MkDocs: the mark, favicons, touch icon.
+ folders.append(("overrides/assets/brand", "brand"))
+ for folder, dest in folders:
source = Path("docs_assets") / folder
if source.is_dir():
- shutil.copytree(source, docs / "assets" / folder, dirs_exist_ok=True)
+ shutil.copytree(source, public / "assets" / dest, dirs_exist_ok=True)
assets_src = Path(".github") / "assets"
for name, dest_name in [("logo.png", "logo.png"), ("favicon.png", "favicon.png")]:
src = assets_src / name
@@ -6572,7 +6675,7 @@ def _generate(args: argparse.Namespace) -> None:
stats = compute_stats(db, coverages, profiles)
stats["composition"] = compute_composition(db)
- write_if_changed(str(docs / "stats.json"), generate_stats(stats))
+ write_if_changed(str(public / "stats.json"), generate_stats(stats))
# Computed once: the gap analysis page and the published gaps export must
# report the same files.
@@ -6611,7 +6714,7 @@ def _generate(args: argparse.Namespace) -> None:
PART_PAGES.add(f"systems/{stem}.md")
write_if_changed(str(docs / "systems" / f"{stem}.md"), page)
write_single_item(
- docs / "api" / "v1" / "systems" / f"{slug}.json", "systems",
+ public / "api" / "v1" / "systems" / f"{slug}.json", "systems",
system_export_item(slug, mfr, consoles),
str(db.get("generated_at") or _timestamp()),
)
@@ -6621,7 +6724,7 @@ def _generate(args: argparse.Namespace) -> None:
print("Generating static API and bulk data exports...")
exports = generate_data_exports(
- docs, db, coverages, profiles, stats, gap_report
+ public, db, coverages, profiles, stats, gap_report
)
write_if_changed(str(docs / "data.md"), generate_data_page(exports) + reference_next())
PAGE_DATA["data.md"] = dataset_structured_data(exports, db)
@@ -6662,7 +6765,7 @@ def _generate(args: argparse.Namespace) -> None:
for name, profile in profiles.items()
if profile.get("type") not in ("alias", "test")
}
- (docs / "api" / "v1" / "emulators").mkdir(parents=True, exist_ok=True)
+ (public / "api" / "v1" / "emulators").mkdir(parents=True, exist_ok=True)
for name, profile in public_profiles.items():
# The profile itself, one small document per core: reusing one
# meant downloading emulators.json (5.9 MB) and filtering it.
@@ -6670,7 +6773,7 @@ def _generate(args: argparse.Namespace) -> None:
# profile validates against the published schema, which an "id"
# beside its fields broke on all 530, and schema_version is there.
write_if_changed(
- str(docs / "api" / "v1" / "emulators" / f"{name}.json"),
+ str(public / "api" / "v1" / "emulators" / f"{name}.json"),
json.dumps(
{
"schema_version": 1,
@@ -6734,7 +6837,12 @@ def _generate(args: argparse.Namespace) -> None:
wiki_src = Path(WIKI_SRC_DIR)
if wiki_src.is_dir():
for src_file in wiki_src.glob("*.md"):
- shutil.copy2(src_file, wiki_dest / src_file.name)
+ if markup.starlight():
+ # Written in Material syntax until the wiki is written for Starlight.
+ write_if_changed(str(wiki_dest / src_file.name),
+ sitestarlight.wiki_page(src_file.read_text(encoding="utf-8")))
+ else:
+ shutil.copy2(src_file, wiki_dest / src_file.name)
_record(wiki_dest / src_file.name)
# data-model.md is generated (contains live DB stats)
write_if_changed(
@@ -6750,15 +6858,39 @@ def _generate(args: argparse.Namespace) -> None:
# Swept before the decoration, which rewrites every page it finds: a
# page no generator produced any more (a console moved back onto its
# manufacturer's page) was decorated, recorded, and kept for ever.
- stale = _sweep_generated(docs, GENERATED_DIRS)
+ stale = _sweep_generated(docs, [d for d in GENERATED_DIRS if d not in PUBLIC_DIRS])
+ stale += _sweep_generated(public, list(PUBLIC_DIRS))
+
+ if markup.starlight():
+ write_if_changed(str(docs / "404.md"), generate_not_found())
print("Adding page metadata and structured data...")
- decorate_markdown_pages(docs)
+ lastmod = decorate_markdown_pages(docs)
+
+ nav = generate_mkdocs_nav(coverages, manufacturers, profiles, registry,
+ Path(WIKI_SRC_DIR))
+ if markup.starlight():
+ print("Writing the Starlight navigation and the picker's data...")
+ site = Path(args.site_dir)
+ present = {
+ path.relative_to(docs).with_suffix(".md").as_posix()
+ for path in docs.rglob("*.md*") if path.suffix in (".md", ".mdx")
+ } - {"404.md"}
+ generated = site / "src" / "generated"
+ generated.mkdir(parents=True, exist_ok=True)
+ write_if_changed(str(generated / "sidebar.json"),
+ json.dumps(sitestarlight.sidebar(nav, present), indent=1) + "\n")
+ write_if_changed(str(generated / "lastmod.json"),
+ json.dumps(dict(sorted(lastmod.items())), indent=1) + "\n")
+ sitestarlight.picker_data(_REPO_ROOT / "install", public / "data" / "picker")
+ total_pages = len(present)
+ print(f"\nGenerated {total_pages} pages in {args.docs_dir}/")
+ if stale:
+ print(f"Removed {stale} page(s) nothing produced this run")
+ return
# Update mkdocs.yml nav section only (avoid yaml.dump round-trip mangling quotes)
print("Updating mkdocs.yml nav...")
- nav = generate_mkdocs_nav(coverages, manufacturers, profiles, registry,
- Path(WIKI_SRC_DIR))
nav_yaml = yaml.dump(
{"nav": nav}, default_flow_style=False, sort_keys=False, allow_unicode=True
)
diff --git a/scripts/sitemarkup.py b/scripts/sitemarkup.py
index 2116b556..14362806 100644
--- a/scripts/sitemarkup.py
+++ b/scripts/sitemarkup.py
@@ -13,6 +13,7 @@ from __future__ import annotations
import html
import re
+from contextlib import contextmanager
ENGINES = ("mkdocs", "starlight")
_engine = "mkdocs"
@@ -39,6 +40,19 @@ def engine() -> str:
return _engine
+@contextmanager
+def using(name: str):
+ """Write in another engine's syntax for a while. A page split by size
+ is measured in the MkDocs Markdown its budgets were tuned on, so both
+ engines cut the same pages at the same files."""
+ previous = _engine
+ set_engine(name)
+ try:
+ yield
+ finally:
+ set_engine(previous)
+
+
def starlight() -> bool:
return _engine == "starlight"
diff --git a/scripts/siterender.py b/scripts/siterender.py
index 0683ad43..16d48ec9 100644
--- a/scripts/siterender.py
+++ b/scripts/siterender.py
@@ -7,6 +7,8 @@ import urllib.parse
from collections.abc import Callable
from pathlib import Path
+import sitemarkup as markup
+
WIKI_SRC_DIR = "wiki" # manually maintained wiki sources
SYSTEM_ICON_BASE = "https://raw.githubusercontent.com/libretro/retroarch-assets/master/xmb/systematic/png"
@@ -89,7 +91,7 @@ def system_icon_markdown(manufacturer: str, console_name: str) -> str:
name = _icon_name(manufacturer, console_name)
if not _icon_available.get(name):
return ""
- return f"}){{ width=24 }} "
+ return markup.image(console_name, _icon_url(name), width="24") + " "
# Global index: maps system_id -> (manufacturer_slug, console_name) for cross-linking
_system_page_map: dict[str, tuple[str, str]] = {}
diff --git a/scripts/sitestarlight.py b/scripts/sitestarlight.py
new file mode 100644
index 00000000..324ae18c
--- /dev/null
+++ b/scripts/sitestarlight.py
@@ -0,0 +1,572 @@
+"""The site as an Astro Starlight project: pages, sidebar and data files.
+
+generate_site.py writes each page body; this module gives it the form
+Starlight reads. A page's links are written as the docs tree holds them
+(``../systems/sony.md``, MkDocs' convention, the one a Markdown editor
+follows), and the site serves them as addresses under /retrobios/, so
+each target is resolved against the page's own place here. Every heading
+keeps the id the MkDocs site gave it, so the anchors cited since stay
+valid.
+"""
+
+from __future__ import annotations
+
+import html
+import json
+import re
+import unicodedata
+import urllib.parse
+from html import unescape
+from pathlib import Path, PurePosixPath
+
+BASE = "/retrobios/"
+SITE_URL = "https://abdess.github.io/retrobios/"
+
+# Components a page body may use, and where each comes from.
+COMPONENTS = {
+ "Tabs": "@astrojs/starlight/components",
+ "TabItem": "@astrojs/starlight/components",
+ "FilePicker": "components/FilePicker.astro",
+}
+
+# Pages of file cards: level 3 headings by the hundred, so the table of
+# contents stops at level 2.
+CARD_PAGES = ("systems/", "emulators/", "platforms/")
+
+FENCE = re.compile(r"(^[ \t]*(`{3,}|~{3,}).*?^[ \t]*\2[ \t]*$)", re.M | re.S)
+CODE_SPAN = re.compile(r"(`+)(?!`)(.+?)(?!?)\[(?P(?:[^\[\]\n]|\[[^\]\n]*\])*)\]\((?P[^)\s]+)(?P \"[^\"]*\")?\)")
+HTML_URL = re.compile(r"(?P\b(?:href|src))=\"(?P[^\"]*)\"")
+ATTR_BLOCK = re.compile(r"[ \t]*\{(?P[^{}]*)\}[ \t]*$")
+ID_ATTR = re.compile(r"\bid=\"(?P[^\"]+)\"")
+IDCOUNT = re.compile(r"^(.*)_([0-9]+)$")
+
+
+# Addresses
+
+
+def page_url(rel: str) -> str:
+ """The address of a page, by its path under the docs tree."""
+ stem = rel.rsplit(".", 1)[0] if rel.endswith((".md", ".mdx")) else rel
+ if stem == "index":
+ return BASE
+ if stem.endswith("/index"):
+ stem = stem[: -len("/index")]
+ return f"{BASE}{stem}/"
+
+
+def resolve(rel_page: str, target: str) -> str:
+ """A link target written relative to the page's file, as a site address."""
+ if not target or re.match(r"^(?:[a-z][a-z0-9+.-]*:|#|/)", target):
+ return target
+ path, hash_mark, fragment = target.partition("#")
+ path, question, query = path.partition("?")
+ parts: list[str] = []
+ for piece in (PurePosixPath(rel_page).parent / path).as_posix().split("/"):
+ if piece in ("", "."):
+ continue
+ if piece == "..":
+ if parts:
+ parts.pop()
+ else:
+ parts.append(piece)
+ joined = "/".join(parts)
+ if joined.endswith((".md", ".mdx")):
+ url = page_url(joined)
+ elif path.endswith("/") or not path:
+ url = f"{BASE}{joined}/" if joined else BASE
+ else:
+ url = f"{BASE}{joined}"
+ if question:
+ url += f"?{query}"
+ return url + (f"#{fragment}" if hash_mark else "")
+
+
+# Text outside code
+
+
+def outside_fences(text: str, fn) -> str:
+ parts = FENCE.split(text)
+ # FENCE has two groups: text, fence, marker, text, ...
+ out = []
+ for index in range(0, len(parts), 3):
+ out.append(fn(parts[index]))
+ if index + 1 < len(parts):
+ out.append(parts[index + 1])
+ return "".join(out)
+
+
+def outside_code(text: str, fn) -> str:
+ def chunk(part: str) -> str:
+ out, position = [], 0
+ for match in CODE_SPAN.finditer(part):
+ out.append(fn(part[position:match.start()]))
+ out.append(match.group(0))
+ position = match.end()
+ out.append(fn(part[position:]))
+ return "".join(out)
+ return outside_fences(text, chunk)
+
+
+def rewrite_links(text: str, rel: str) -> str:
+ def markdown(match: re.Match) -> str:
+ return (f"{match['bang']}[{match['text']}]({resolve(rel, match['url'])}"
+ f"{match['title'] or ''})")
+
+ def attribute(match: re.Match) -> str:
+ return f'{match["attr"]}="{resolve(rel, match["url"])}"'
+
+ text = outside_fences(text, lambda chunk: MD_LINK.sub(markdown, chunk))
+ return outside_fences(text, lambda chunk: HTML_URL.sub(attribute, chunk))
+
+
+# Heading ids, as Python-Markdown's toc extension gives them
+
+
+def toc_slug(text: str) -> str:
+ text = re.sub(r"<[^>]+>", "", text)
+ text = re.sub(r"!\[[^\]]*\]\([^)]*\)(\{[^}]*\})?", "", text)
+ text = re.sub(r"\[([^\]]*)\]\([^)]*\)", r"\1", text)
+ text = unescape(text.replace("`", "").replace("**", "").replace("\\", ""))
+ text = unicodedata.normalize("NFKD", text).encode("ascii", "ignore").decode("ascii")
+ text = re.sub(r"[^\w\s-]", "", text).strip().lower()
+ return re.sub(r"[-\s]+", "-", text)
+
+
+def _unique(ident: str, used: set[str]) -> str:
+ while ident in used or not ident:
+ match = IDCOUNT.match(ident)
+ ident = f"{match[1]}_{int(match[2]) + 1}" if match else f"{ident}_1"
+ used.add(ident)
+ return ident
+
+
+def pin_heading_ids(text: str, title_id: str = "") -> str:
+ """Every heading with the id the MkDocs build gave it. *title_id* is
+ the page title's, taken first."""
+ used: set[str] = {title_id} if title_id else set()
+ explicit = set()
+ for match in re.finditer(r"\{[^{}\n]*#([^\s}]+)[^{}\n]*\}[ \t]*$", text, re.M):
+ explicit.add(match[1])
+ for match in ID_ATTR.finditer(text):
+ explicit.add(match["id"])
+ used |= explicit
+
+ def markdown(match: re.Match) -> str:
+ heading = match["mdtext"]
+ attrs = ATTR_BLOCK.search(heading)
+ body = heading[: attrs.start()] if attrs else heading
+ tokens = attrs["attrs"].split() if attrs else []
+ if any(token.startswith("#") for token in tokens):
+ return match.group(0)
+ ident = _unique(toc_slug(body), used)
+ return f"{match['hashes']} {body.rstrip()} {{ {' '.join(['#' + ident, *tokens])} }}"
+
+ def raw(match: re.Match) -> str:
+ if ID_ATTR.search(match["attrs"]):
+ return match.group(0)
+ level, text = match["level"], match["htmltext"]
+ ident = _unique(toc_slug(text), used)
+ return f'{text} '
+
+ def pick(match: re.Match) -> str:
+ return markdown(match) if match["hashes"] else raw(match)
+
+ # In document order: one pass over either kind of heading.
+ return outside_fences(text, lambda chunk: _ANY_HEADING.sub(pick, chunk))
+
+
+_ANY_HEADING = re.compile(
+ r"^(?P#{2,6})[ \t]+(?P.+?)[ \t]*$"
+ r"|[2-6])(?P(?:\s[^>]*)?)>(?P.*?) ",
+ re.M | re.S,
+)
+
+
+# Glossary tooltips
+
+
+def glossary_terms(path: Path) -> dict[str, str]:
+ """Each term the glossary defines, with its definition as plain text."""
+ terms: dict[str, str] = {}
+ if not path.is_file():
+ return terms
+ text = path.read_text(encoding="utf-8")
+ for match in re.finditer(r"^(?P\*\*.+)\n: (?P.+(?:\n .+)*)", text, re.M):
+ definition = re.sub(r"\s+", " ", match["body"]).strip()
+ definition = re.sub(r"\[([^\]]+)\]\([^)]*\)", r"\1", definition).replace("`", "")
+ for term in re.findall(r"\*\*([^*]+)\*\*", match["term"]):
+ terms.setdefault(term, definition)
+ return terms
+
+
+_SKIP_TAGS = re.compile(r"<(a|code|pre|abbr|h[1-6]|script|style|summary|svg|figure)\b.*?\1>", re.S | re.I)
+
+
+def add_tooltips(text: str, terms: dict[str, str]) -> str:
+ """The first use of each glossary term on a page, with its definition
+ a hover or a tap away. Headings, links, code and HTML are left as they are."""
+ if not terms:
+ return text
+ pending = dict(terms)
+ pattern = re.compile(
+ r"(? str:
+ if not pending:
+ return chunk
+ out = []
+ for line in chunk.split("\n"):
+ stripped = line.lstrip()
+ if (not pending or stripped.startswith(("#", "|", "<", ":::", "[", "!"))
+ or not stripped):
+ out.append(line)
+ continue
+ out.append(_tooltip_line(line, pattern, pending))
+ return "\n".join(out)
+
+ return outside_code(text, prose)
+
+
+def _tooltip_line(line: str, pattern: re.Pattern, pending: dict[str, str]) -> str:
+ # Inline HTML, links and emphasis markers stay whole.
+ protected = re.compile(r"<[^>]+>|\[[^\]]*\]\([^)]*\)|\*\*[^*]+\*\*|\{[^}]*\}")
+ out, position = [], 0
+ for match in protected.finditer(line):
+ out.append(_tooltip_text(line[position:match.start()], pattern, pending))
+ out.append(match.group(0))
+ position = match.end()
+ out.append(_tooltip_text(line[position:], pattern, pending))
+ return "".join(out)
+
+
+def _tooltip_text(text: str, pattern: re.Pattern, pending: dict[str, str]) -> str:
+ def swap(match: re.Match) -> str:
+ term = match.group(1)
+ if term not in pending:
+ return term
+ definition = pending.pop(term)
+ return f'{term} '
+ return pattern.sub(swap, text)
+
+
+# MDX
+
+
+_VOID = re.compile(r"<(br|img|input|hr|source|meta|link|wbr)\b([^>]*?)(?")
+_HEADING_ATTRS = re.compile(r"(?m)(?<=\S)[ \t]+\{[ \t]*#[^{}\n]*\}[ \t]*$")
+_COMMENT = re.compile(r"", re.S)
+
+
+def mdx_safe(text: str) -> str:
+ """What MDX would read as code in prose: braces, a stray ``<``,
+ comments, unclosed void tags and ```` links."""
+ def fix(chunk: str) -> str:
+ chunk = _COMMENT.sub("", chunk)
+ chunk = re.sub(r"<(https?://[^>\s]+)>", r"[\1](\1)", chunk)
+ held: list[str] = []
+
+ def hold(match: re.Match) -> str:
+ held.append(match.group(0))
+ return f"\x00{len(held) - 1}\x00"
+ chunk = _HEADING_ATTRS.sub(hold, chunk)
+ chunk = chunk.replace("{", "{").replace("}", "}")
+ chunk = re.sub(r"\x00(\d+)\x00", lambda m: held[int(m[1])], chunk)
+ chunk = re.sub(r"<(?![A-Za-z/])", "<", chunk)
+ return _VOID.sub(lambda m: f"<{m[1]}{m[2].rstrip()} />", chunk)
+ return outside_code(text, fix)
+
+
+def used_components(body: str) -> list[str]:
+ return [name for name in COMPONENTS if re.search(rf"<{name}[\s/>]", body)]
+
+
+# Pages
+
+
+def _front_matter(data: dict) -> str:
+ import yaml
+
+ return "---\n" + yaml.safe_dump(data, sort_keys=False, allow_unicode=True, width=1000) + "---\n\n"
+
+
+def og_section(rel: str, level: str) -> str:
+ """The sharing card a page takes: its level, or what its address says."""
+ url = page_url(rel)[len(BASE):]
+ if not url:
+ return "home"
+ if level in ("start", "setup", "explore", "reference", "contribute"):
+ return level
+ if url.startswith(("which-pack", "wiki/first-install", "wiki/faq", "wiki/getting-started")):
+ return "start"
+ if url.startswith("platforms"):
+ return "setup"
+ if url.startswith(("systems", "emulators", "files")):
+ return "explore"
+ if url.startswith(("contributing", "wiki/adding", "wiki/profiling", "wiki/testing", "wiki/release")):
+ return "contribute"
+ return "reference"
+
+
+def page_head(rel: str, level: str, title: str, browser_title: str, structured: dict) -> list[dict]:
+ """What a page adds to its head: its own window title, the sharing
+ card of its section, and its structured data."""
+ section = og_section(rel, level)
+ image = f"{SITE_URL}assets/og/{section}.png"
+ head: list[dict] = []
+ if browser_title != title:
+ head.append({"tag": "title", "content": f"{browser_title} - RetroBIOS"})
+ head += [
+ {"tag": "meta", "attrs": {"property": "og:image", "content": image}},
+ {"tag": "meta", "attrs": {"property": "og:image:width", "content": "1200"}},
+ {"tag": "meta", "attrs": {"property": "og:image:height", "content": "630"}},
+ {"tag": "meta", "attrs": {"property": "og:image:alt", "content": f"RetroBIOS: {browser_title}"}},
+ {"tag": "meta", "attrs": {"name": "twitter:image", "content": image}},
+ {"tag": "script", "attrs": {"type": "application/ld+json"},
+ "content": json.dumps(structured, ensure_ascii=False, separators=(",", ":")).replace("", "<\\/")},
+ ]
+ return head
+
+
+def split_title(markdown: str) -> tuple[str, str, str]:
+ """The page's H1 as plain text, the id MkDocs gave it, and the body
+ without it."""
+ match = re.search(r"^# (.+?)[ \t]*$\n?", markdown, re.M)
+ if not match:
+ return "", "", markdown
+ explicit = re.search(r"\{[^}]*#([^\s}]+)[^}]*\}\s*$", match[1])
+ title = re.sub(r"\s*\{[^}]*\}\s*$", "", match[1])
+ ident = explicit[1] if explicit else toc_slug(title)
+ title = re.sub(r"<[^>]+>", "", title)
+ title = re.sub(r"!\[[^\]]*\]\([^)]*\)", "", title)
+ title = re.sub(r"\[([^\]]*)\]\([^)]*\)", r"\1", title).replace("`", "")
+ return unescape(title).strip(), ident, markdown[: match.start()] + markdown[match.end():]
+
+
+def finalize_page(
+ rel: str,
+ markdown: str,
+ *,
+ level: str,
+ boost: int | None,
+ browser_title: str,
+ description: str,
+ structured: dict,
+ terms: dict[str, str],
+ search: bool = True,
+) -> tuple[str, str]:
+ """The Starlight file for a page body: its path under the content
+ collection and its text."""
+ title, title_id, body = split_title(markdown)
+ body = body.lstrip("\n")
+ body = rewrite_links(body, rel)
+ body = pin_heading_ids(body, title_id)
+ if title_id:
+ # The title is the theme's, with an id of its own: the one MkDocs
+ # gave it stays an anchor at the top of the text.
+ body = f'
\n\n' + body
+ if rel not in ("wiki/glossary.md", "wiki/reference-terms.md"):
+ body = add_tooltips(body, terms)
+ components = used_components(body)
+ stem = rel.rsplit(".", 1)[0]
+ if components:
+ body = mdx_safe(body)
+ imports: dict[str, list[str]] = {}
+ for name in components:
+ imports.setdefault(COMPONENTS[name], []).append(name)
+ # Our own components are imported from src/, two levels above the
+ # content collection plus the page's own folders.
+ up = "../" * (rel.count("/") + 2)
+ body = "".join(f"import {{ {', '.join(names)} }} from '{source}';\n"
+ if source.startswith("@") else f"import {names[0]} from '{up}{source}';\n"
+ for source, names in imports.items()) + "\n" + body
+ front: dict = {"title": title or browser_title, "description": description}
+ # Starlight's slugger drops dots and plus signs: the address stays MkDocs'.
+ if re.search(r"[^a-z0-9_/-]", stem):
+ front["slug"] = stem
+ if level:
+ front["level"] = level
+ if boost:
+ front["boost"] = boost
+ if rel.startswith(CARD_PAGES):
+ front["tableOfContents"] = {"minHeadingLevel": 2, "maxHeadingLevel": 2}
+ if not search:
+ front["pagefind"] = False
+ front["head"] = page_head(rel, level, front["title"], browser_title, structured)
+ if structured.get("dateModified"):
+ front["lastmod"] = str(structured["dateModified"])
+ return stem + (".mdx" if components else ".md"), _front_matter(front) + body
+
+
+# Material syntax in the hand-written wiki pages, read until they are
+# written for Starlight.
+
+_WIKI_ADMONITION = re.compile(r'^(?P *)(?P!!!|\?\?\?\+?) *(?P[a-z]+)(?: +"(?P[^"]*)")? *$')
+_WIKI_TAB = re.compile(r'^(?P *)=== +"(?P[^"]+)" *$')
+_WIKI_BUTTON = re.compile(r"\[(?P[^\]]+)\]\((?P[^)\s]+)\)\{ *(?P[^}]*)\}")
+
+
+def _indented_block(lines: list[str], start: int, indent: int) -> tuple[list[str], int]:
+ body: list[str] = []
+ index = start
+ pad = " " * (indent + 4)
+ while index < len(lines) and (not lines[index].strip() or lines[index].startswith(pad)):
+ body.append(lines[index][indent + 4:] if lines[index].startswith(pad) else "")
+ index += 1
+ while body and not body[-1].strip():
+ body.pop()
+ index -= 1
+ return body, index
+
+
+def wiki_page(text: str) -> str:
+ """A wiki page in Material syntax, its blocks written for Starlight."""
+ import sitemarkup as markup
+
+ def blocks(lines: list[str]) -> list[str]:
+ out: list[str] = []
+ index = 0
+ while index < len(lines):
+ line = lines[index]
+ admonition = _WIKI_ADMONITION.match(line)
+ tab = _WIKI_TAB.match(line)
+ if admonition:
+ indent = len(admonition["indent"])
+ body, index = _indented_block(lines, index + 1, indent)
+ title = admonition["title"] if admonition["title"] is not None else admonition["type"].capitalize()
+ pad = " " * indent
+ if admonition["kind"] == "!!!":
+ out += markup.aside(admonition["type"], title, blocks(body), pad)
+ else:
+ out += markup.details(admonition["type"], title, blocks(body), pad,
+ opened=admonition["kind"].endswith("+"))
+ continue
+ if tab:
+ indent = len(tab["indent"])
+ items = []
+ while index < len(lines):
+ tab = _WIKI_TAB.match(lines[index])
+ if not tab or len(tab["indent"]) != indent:
+ break
+ body, index = _indented_block(lines, index + 1, indent)
+ items.append((tab["label"], blocks(body)))
+ while index < len(lines) and not lines[index].strip() and index + 1 < len(lines) \
+ and _WIKI_TAB.match(lines[index + 1]):
+ index += 1
+ out += markup.tabs(items, " " * indent)
+ continue
+ out.append(line)
+ index += 1
+ return out
+
+ def button(match: re.Match) -> str:
+ classes = match["attrs"].split()
+ if ".md-button" not in classes:
+ return match.group(0)
+ return markup.button(match["text"], match["url"], primary=".md-button--primary" in classes)
+
+ text = "\n".join(blocks(text.split("\n")))
+ return outside_fences(text, lambda chunk: _WIKI_BUTTON.sub(button, chunk))
+
+
+# Navigation
+
+
+def _sidebar_link(rel: str) -> str:
+ return page_url(rel)[len(BASE) - 1:]
+
+
+def sidebar(nav: list, present: set[str]) -> dict:
+ """The navigation as Starlight topics, one per tab.
+
+ A page the navigation does not list (the parts of a split page) joins
+ the topic that holds most pages of its directory.
+ """
+ owner: dict[str, str] = {}
+
+ def item(entry, topic: str) -> dict | None:
+ if isinstance(entry, str):
+ owner[entry] = topic
+ return {"label": "Overview", "link": _sidebar_link(entry)} if entry in present else None
+ (label, value), = entry.items()
+ if isinstance(value, str):
+ if value.startswith("http"):
+ return {"label": label, "link": value}
+ owner[value] = topic
+ return {"label": label, "link": _sidebar_link(value)} if value in present else None
+ children = [child for child in (item(v, topic) for v in value) if child]
+ return {"label": label, "items": children, "collapsed": True} if children else None
+
+ topics = []
+ for tab in nav:
+ (label, value), = tab.items()
+ topic = re.sub(r"[^a-z]+", "-", label.lower()).strip("-")
+ children = [child for child in (item(v, topic) for v in value) if child]
+ if children:
+ topics.append({"id": topic, "label": label, "link": next(_links(children))["link"],
+ "items": children})
+
+ votes: dict[str, dict[str, int]] = {}
+ for rel, topic in owner.items():
+ tally = votes.setdefault(rel.rpartition("/")[0], {})
+ tally[topic] = tally.get(topic, 0) + 1
+ extra: dict[str, list[str]] = {}
+ # The page a stale link lands on belongs to no tab.
+ exclude: list[str] = ["/404"]
+ for rel in sorted(present - owner.keys()):
+ tally = votes.get(rel.rpartition("/")[0])
+ path = "/" + (_sidebar_link(rel).strip("/") or "index")
+ if tally:
+ extra.setdefault(max(tally, key=tally.get), []).append(path)
+ else:
+ exclude.append(path)
+ return {"topics": topics, "options": {"topics": extra, "exclude": exclude}}
+
+
+def _links(items: list[dict]):
+ for entry in items:
+ if "link" in entry:
+ yield entry
+ else:
+ yield from _links(entry["items"])
+
+
+# The picker's data
+
+
+def picker_data(install: Path, out: Path) -> int:
+ """Each manifest, compacted to what the file picker reads: per file its
+ destination, size and systems, the systems install.py --system accepts.
+ """
+ out.mkdir(parents=True, exist_ok=True)
+ index = []
+ for manifest in sorted(install.glob("*.json")):
+ data = json.loads(manifest.read_text(encoding="utf-8"))
+ if not isinstance(data, dict) or "files" not in data or "platform" not in data:
+ continue
+ systems = sorted({s for f in data["files"] for s in f.get("systems") or []})
+ position = {s: k for k, s in enumerate(systems)}
+ names = data.get("system_names") or {}
+ payload = {
+ "platform": data["platform"],
+ "name": data.get("display_name", data["platform"]),
+ "folder": (data.get("detect") or [{}])[0].get("bios_path", ""),
+ "generated": data.get("generated", ""),
+ "systems": [{"id": s, "native": names.get(s, [])} for s in systems],
+ "other": any(not f.get("systems") for f in data["files"]),
+ "files": [[f["dest"], f.get("size", 0), [position[s] for s in f.get("systems") or []]]
+ for f in sorted(data["files"], key=lambda f: f["dest"])],
+ }
+ (out / f"{data['platform']}.json").write_text(
+ json.dumps(payload, separators=(",", ":")), encoding="utf-8")
+ index.append({"platform": data["platform"], "name": payload["name"],
+ "files": len(payload["files"])})
+ index.sort(key=lambda entry: entry["name"].casefold())
+ (out / "index.json").write_text(json.dumps(index, separators=(",", ":")), encoding="utf-8")
+ return len(index)
+
+
+def quote_url(url: str) -> str:
+ return urllib.parse.quote(url, safe="/:#?=&%")
diff --git a/website/.gitignore b/website/.gitignore
new file mode 100644
index 00000000..4220bff9
--- /dev/null
+++ b/website/.gitignore
@@ -0,0 +1,7 @@
+node_modules/
+dist/
+.astro/
+# Written by scripts/generate_site.py
+src/content/docs/
+src/generated/
+public/
diff --git a/website/astro.config.mjs b/website/astro.config.mjs
new file mode 100644
index 00000000..3101060d
--- /dev/null
+++ b/website/astro.config.mjs
@@ -0,0 +1,85 @@
+// The RetroBIOS site. Pages, navigation and data files are written by
+// scripts/generate_site.py; this folder holds the engine's configuration,
+// the components and the styles.
+import { copyFile } from 'node:fs/promises';
+import { fileURLToPath } from 'node:url';
+import { defineConfig } from 'astro/config';
+import starlight from '@astrojs/starlight';
+import sitemap from '@astrojs/sitemap';
+import { satteri } from '@astrojs/markdown-satteri';
+import starlightSidebarTopics from 'starlight-sidebar-topics';
+import starlightLinksValidator from 'starlight-links-validator';
+import sidebar from './src/generated/sidebar.json' with { type: 'json' };
+import lastmod from './src/generated/lastmod.json' with { type: 'json' };
+
+const SITE = 'https://abdess.github.io';
+const BASE = '/retrobios/';
+
+// The sitemap keeps the address MkDocs gave it: sitemap.xml, beside the index.
+const sitemapAlias = {
+ name: 'retrobios-sitemap-alias',
+ hooks: {
+ 'astro:build:done': async ({ dir }) => {
+ const root = fileURLToPath(dir);
+ await copyFile(`${root}sitemap-index.xml`, `${root}sitemap.xml`);
+ },
+ },
+};
+
+export default defineConfig({
+ site: SITE,
+ base: BASE,
+ trailingSlash: 'always',
+ markdown: {
+ processor: satteri({
+ features: {
+ headingAttributes: true,
+ definitionList: true,
+ // Straight quotes: curly ones would change a copied command.
+ smartPunctuation: false,
+ },
+ }),
+ },
+ integrations: [
+ starlight({
+ title: 'RetroBIOS',
+ titleDelimiter: '-',
+ description:
+ 'Source-verified BIOS and firmware packs for RetroArch, Batocera, Recalbox, Lakka, RetroPie, EmuDeck, RetroBat, RetroDECK, RomM, BizHawk, ROCKNIX, KNULLI, AmberELEC, EmuELEC and MiSTer FPGA.',
+ // The brand mark is white on the indigo of the old header: indigo on a light page.
+ logo: { light: './src/assets/logo-light.svg', dark: './src/assets/logo-dark.svg', replacesTitle: false },
+ favicon: '/assets/brand/favicon-32.png',
+ head: [
+ { tag: 'link', attrs: { rel: 'icon', type: 'image/svg+xml', href: `${BASE}assets/brand/favicon.svg` } },
+ { tag: 'link', attrs: { rel: 'apple-touch-icon', sizes: '180x180', href: `${BASE}assets/brand/apple-touch-icon.png` } },
+ ],
+ social: [{ icon: 'github', label: 'RetroBIOS on GitHub', href: 'https://github.com/Abdess/retrobios' }],
+ customCss: ['./src/styles/retrobios.css'],
+ components: {
+ PageTitle: './src/components/PageTitle.astro',
+ MarkdownContent: './src/components/MarkdownContent.astro',
+ Footer: './src/components/Footer.astro',
+ },
+ routeMiddleware: ['./src/routeData.ts'],
+ tableOfContents: { minHeadingLevel: 2, maxHeadingLevel: 3 },
+ // Commands wrap instead of scrolling, so the end of a one-liner stays visible.
+ expressiveCode: { defaultProps: { wrap: true } },
+ lastUpdated: false,
+ pagination: true,
+ plugins: [
+ starlightSidebarTopics(sidebar.topics, sidebar.options),
+ starlightLinksValidator(),
+ ],
+ }),
+ // A page that states its own date (an emulator page: the day its
+ // profile was last read against the source) gives it to the sitemap.
+ // The others get no lastmod rather than the build date.
+ sitemap({
+ serialize(item) {
+ const date = lastmod[new URL(item.url).pathname];
+ return date ? { ...item, lastmod: date } : item;
+ },
+ }),
+ sitemapAlias,
+ ],
+});
diff --git a/website/package-lock.json b/website/package-lock.json
new file mode 100644
index 00000000..06829b5d
--- /dev/null
+++ b/website/package-lock.json
@@ -0,0 +1,5403 @@
+{
+ "name": "retrobios-site",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "retrobios-site",
+ "dependencies": {
+ "@astrojs/markdown-satteri": "0.4.5",
+ "@astrojs/sitemap": "3.7.4",
+ "@astrojs/starlight": "0.42.6",
+ "astro": "7.4.2",
+ "starlight-links-validator": "0.27.0",
+ "starlight-sidebar-topics": "0.9.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding/-/compiler-binding-0.5.2.tgz",
+ "integrity": "sha512-lEn76+0JcMQ5RDXU0wSMWJg2ZppiZvSULpghC29cfv4o9bhvWFDbQjsCtvjjXN9iVLFQ7fL3wTR2L2UU13mP8g==",
+ "license": "MIT",
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@astrojs/compiler-binding-android-arm64": "0.5.2",
+ "@astrojs/compiler-binding-darwin-arm64": "0.5.2",
+ "@astrojs/compiler-binding-darwin-x64": "0.5.2",
+ "@astrojs/compiler-binding-linux-arm64-gnu": "0.5.2",
+ "@astrojs/compiler-binding-linux-arm64-musl": "0.5.2",
+ "@astrojs/compiler-binding-linux-x64-gnu": "0.5.2",
+ "@astrojs/compiler-binding-linux-x64-musl": "0.5.2",
+ "@astrojs/compiler-binding-wasm32-wasi": "0.5.2",
+ "@astrojs/compiler-binding-win32-arm64-msvc": "0.5.2",
+ "@astrojs/compiler-binding-win32-x64-msvc": "0.5.2"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-android-arm64": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-android-arm64/-/compiler-binding-android-arm64-0.5.2.tgz",
+ "integrity": "sha512-kckIxon6Rm/Bxzh9mGPYpRRqUbToUQoHptCmpMmE+Yt0Xumq8CqrKUidyYhrBsrHVO7bxuRzzWUUqvpf+iYceQ==",
+ "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.2",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-arm64/-/compiler-binding-darwin-arm64-0.5.2.tgz",
+ "integrity": "sha512-1ZG5Ar3dD8HtodX9XH1mRmE0qBXZlvjYXSFfpgFwocqfWnvhFuqtGUNseL8wgfImiRMztIjpQqlogQFQQtgiag==",
+ "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.2",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-x64/-/compiler-binding-darwin-x64-0.5.2.tgz",
+ "integrity": "sha512-+kl35S7hiHJ2cb7eujwEsPFYYuSgE11uEE2Z20lAJ+undpgulMcW+weQqFPOSSZccEKHOiLIY+kQHkb4e4092w==",
+ "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.2",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-gnu/-/compiler-binding-linux-arm64-gnu-0.5.2.tgz",
+ "integrity": "sha512-BqlR0EHNQLPLwgL9+xogjsAc7zGf0cHho2CPdhv0P1yqbVMwGVaGAqrANP6VYAXCqSC43AF4Czcxc2n+oqdesg==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "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.2",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-musl/-/compiler-binding-linux-arm64-musl-0.5.2.tgz",
+ "integrity": "sha512-GRGyby3AZWw5d6JQzidyxPyvGgHWsSusrpgKyFVpDTKctABF7L3Pd5V1cRJbV36/rcu+Eh292/wL7pF7MkmGkQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "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.2",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-gnu/-/compiler-binding-linux-x64-gnu-0.5.2.tgz",
+ "integrity": "sha512-9XAqGTMQAtIslMsjK3xdyY6OI9d59OZqcLBB6pvD/DcoR0daYswfrKx2azdDERDY4AqAnWPC7odVM1BmxYv3Vw==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "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.2",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-musl/-/compiler-binding-linux-x64-musl-0.5.2.tgz",
+ "integrity": "sha512-jOViewGeTh9bSfS8vK72ECxoy2yvmA+Pd/ZcEvBI5c7+7IcfHwZ5qA4L9fRYuOhSrAifvQ2dV4vm1XAtloaWBw==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-wasm32-wasi": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-wasm32-wasi/-/compiler-binding-wasm32-wasi-0.5.2.tgz",
+ "integrity": "sha512-Z7p64doar1uvR5C9Xs6j1CV9lGlPVQX7OQ2aAtZ/xKC5aGS6GB13nyc2UfQXTQLvZrvVwsyaflTQy6ziY5Bekw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "1.11.3",
+ "@emnapi/runtime": "1.11.3",
+ "@napi-rs/wasm-runtime": "^1.2.5"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-binding-win32-arm64-msvc": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-arm64-msvc/-/compiler-binding-win32-arm64-msvc-0.5.2.tgz",
+ "integrity": "sha512-Tr13bh+ahTWsjXjzctK7pFpWV+dUU4FtBfETlR7gO+ueex0F+RDch7eRr0YfKysaRhk9tWm14lsCJ5uWyFPNHQ==",
+ "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.2",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-x64-msvc/-/compiler-binding-win32-x64-msvc-0.5.2.tgz",
+ "integrity": "sha512-SfKkI+jOQYLh6wojPvVPV0lFfQnxisweN9B+qCzRvZp7DHAuGif8Y0WcC5PUgN4jb8VKy57LggQDtggR46Du1g==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/compiler-rs": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler-rs/-/compiler-rs-0.5.2.tgz",
+ "integrity": "sha512-IvwYLB8t1M86rKO4oPdBi9GP4gzXcgrVlNgocjwos1jeeUuEIV8d9Crn1PvYD/5IKteWOxpIHomf7e8iYlkE+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/compiler-binding": "0.5.2"
+ },
+ "engines": {
+ "node": ">=22.12.0"
+ }
+ },
+ "node_modules/@astrojs/internal-helpers": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.13.0.tgz",
+ "integrity": "sha512-20jxQqnSmpskcGyad7N5l6Xf1gz0UCa6lNde44bEBdaXGpncEYfAZJ142r2ZgzBaG4dUyzGmhqZWv1INU2eiHQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.5",
+ "@types/mdast": "^4.0.4",
+ "picomatch": "^4.0.7",
+ "retext-smartypants": "^6.2.0",
+ "shiki": "^4.5.0",
+ "smol-toml": "^1.9.1",
+ "unified": "^11.0.5",
+ "yaml": "^2.9.1"
+ }
+ },
+ "node_modules/@astrojs/markdown-satteri": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/@astrojs/markdown-satteri/-/markdown-satteri-0.4.5.tgz",
+ "integrity": "sha512-0IeGdoFbv0f+442KJhYS44Wg8F3MuHmRsxIEYuFyqkjRubc6dPBFZ7azor5t4AeXLQoT5A5h9HyViKpd70Dl9w==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/internal-helpers": "0.13.0",
+ "@astrojs/prism": "4.1.1",
+ "github-slugger": "^2.0.0",
+ "satteri": "^0.10.5"
+ }
+ },
+ "node_modules/@astrojs/mdx": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-8.1.1.tgz",
+ "integrity": "sha512-jEJUJsO2KqmKfBXaKeDHDCCj+Kerj1Fj+XndHznF9wxahvs+1onYCBeKmieM28N2+uA6mW57DOQQw5YaDLdBnA==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/internal-helpers": "0.13.0",
+ "@astrojs/markdown-satteri": "0.4.5",
+ "es-module-lexer": "^3.0.3"
+ },
+ "engines": {
+ "node": ">=22.23.4"
+ },
+ "peerDependencies": {
+ "@astrojs/markdown-remark": "^7.3.0",
+ "@astrojs/markdown-satteri": "^0.4.0",
+ "astro": "^7.2.10"
+ },
+ "peerDependenciesMeta": {
+ "@astrojs/markdown-remark": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@astrojs/prism": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-4.1.1.tgz",
+ "integrity": "sha512-sXlAXJX6t4QfuLg+CuCDzpg7NoxWcyb4paFprgtFppLeisCBmh935h+tJ5CWXfsXEoW2IDy/ihbRMtiu28Chjw==",
+ "license": "MIT",
+ "dependencies": {
+ "prismjs": "^1.30.0"
+ },
+ "engines": {
+ "node": ">=22.23.4"
+ }
+ },
+ "node_modules/@astrojs/sitemap": {
+ "version": "3.7.4",
+ "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.7.4.tgz",
+ "integrity": "sha512-LbKNC24bdUWcQf/pThB6qLlSqHojxGjZDURIzFocY8rlWnAn2t74nnhnK6S5x0NHriHoAduLEpVjRykmeGiVvA==",
+ "license": "MIT",
+ "dependencies": {
+ "sitemap": "^9.0.0",
+ "zod": "^4.3.6"
+ }
+ },
+ "node_modules/@astrojs/starlight": {
+ "version": "0.42.6",
+ "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.42.6.tgz",
+ "integrity": "sha512-Cfs7GdgzgNRCauDxQVsFUVL1thrGHK/rQdbv/SURv/xhGBXx+j5ypOHW9ENPraQxbIzUC8KAnJV7L9mvEVRuyQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/markdown-satteri": "^0.4.0",
+ "@astrojs/mdx": "^8.0.0",
+ "@astrojs/sitemap": "^3.7.3",
+ "@pagefind/default-ui": "^1.3.0",
+ "@types/hast": "^3.0.5",
+ "@types/js-yaml": "^4.0.9",
+ "@types/mdast": "^4.0.4",
+ "astro-expressive-code": "^0.44.0",
+ "bcp-47": "^2.1.0",
+ "hast-util-format": "^1.1.0",
+ "hast-util-select": "^6.0.4",
+ "hast-util-to-html": "^9.0.5",
+ "hast-util-to-string": "^3.0.1",
+ "hastscript": "^9.0.1",
+ "i18next": "^26.0.7",
+ "js-yaml": "^4.1.1",
+ "klona": "^2.0.6",
+ "magic-string": "^1.2.3",
+ "mdast-util-directive": "^3.1.0",
+ "mdast-util-to-markdown": "^2.1.2",
+ "mdast-util-to-string": "^4.0.0",
+ "pagefind": "^1.5.2",
+ "remark-directive": "^4.0.0",
+ "satteri": "^0.10.3",
+ "ultrahtml": "^1.6.0",
+ "unified": "^11.0.5",
+ "unist-util-visit": "^5.1.0",
+ "vfile": "^6.0.3"
+ },
+ "peerDependencies": {
+ "@astrojs/markdown-remark": "^7.3.0",
+ "astro": "^7.2.10"
+ },
+ "peerDependenciesMeta": {
+ "@astrojs/markdown-remark": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@astrojs/telemetry": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.4.1.tgz",
+ "integrity": "sha512-SMkvMsGOpM0FMAoT5pzhgI3LZC0JSPWf7Evwop+fZ3N6UkusPM7lI+sOJjMNI5OJrVoiei1/AGo1cY7sikTc5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ci-info": "^4.4.0",
+ "dset": "^3.1.4",
+ "is-docker": "^4.0.0",
+ "package-manager-detector": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=22.23.4"
+ }
+ },
+ "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"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "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"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "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"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "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"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "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/@ctrl/tinycolor": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.2.1.tgz",
+ "integrity": "sha512-Vh5uy9Y1JBMId6J1f68TMXnZrTCvNdo9Fu2k0d1YnJngR+sM5kWTKl441c/3TFs+cG0NWOjRWgQ/iaWhbYx+7A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "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==",
+ "license": "MIT",
+ "optional": true,
+ "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==",
+ "license": "MIT",
+ "optional": true,
+ "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==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@expressive-code/core": {
+ "version": "0.44.3",
+ "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.44.3.tgz",
+ "integrity": "sha512-pffktF0TJoxC08dgNcutl/0LcSnTrjF6vXR39qVpwZ0EyxpwO9zjI7dbni6sO7w3yHpRBO0voLFvhzB+EPJRsA==",
+ "license": "MIT",
+ "dependencies": {
+ "@ctrl/tinycolor": "^4.0.4",
+ "hast-util-select": "^6.0.2",
+ "hast-util-to-html": "^9.0.1",
+ "hast-util-to-text": "^4.0.1",
+ "hastscript": "^9.0.0",
+ "postcss": "^8.4.38",
+ "postcss-nested": "^7.0.2",
+ "unist-util-visit": "^5.0.0",
+ "unist-util-visit-parents": "^6.0.1"
+ }
+ },
+ "node_modules/@expressive-code/plugin-frames": {
+ "version": "0.44.3",
+ "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.44.3.tgz",
+ "integrity": "sha512-ysW4VXk9vXEliA99Ur6ApUepVtx74sPolp6o/j45BRjZQoL5O6kXKMhSP4koQLih8HbC5B3ImIvO+HKKhkv/oA==",
+ "license": "MIT",
+ "dependencies": {
+ "@expressive-code/core": "^0.44.3"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki": {
+ "version": "0.44.3",
+ "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.44.3.tgz",
+ "integrity": "sha512-jAibAZPG7lDmi61EFJADZjoSZzjHiwZZdbbgR/mTlWXM4ON/Pyo/9tB2vrqQNKylsci6jqtCi6idrwqQXF53xA==",
+ "license": "MIT",
+ "dependencies": {
+ "@expressive-code/core": "^0.44.3",
+ "shiki": "^4.0.2"
+ }
+ },
+ "node_modules/@expressive-code/plugin-text-markers": {
+ "version": "0.44.3",
+ "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.44.3.tgz",
+ "integrity": "sha512-4Ae2xRSDV5kdWPsi40xWbOtoihbzM0zLVjQKkDAnD5KzFXl+6/kd+VhuWOlTRVaJ5cqGk++vCbAaf0MR1XbZlw==",
+ "license": "MIT",
+ "dependencies": {
+ "@expressive-code/core": "^0.44.3"
+ }
+ },
+ "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"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "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"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "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"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "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"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "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"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "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"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "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"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "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"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "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"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "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"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "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"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "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"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "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"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "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"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "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"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "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"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "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.5",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.5.tgz",
+ "integrity": "sha512-HshSvXzmBxNzqejd3k/KemgQ3hdREUYFRRrgxOZ+gPljDIsO2SDZrVZMd4Z/HsxudQbE62OVyVFE8z9BCurfjA==",
+ "license": "MIT",
+ "optional": true,
+ "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.155.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.155.0.tgz",
+ "integrity": "sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/oxc-project"
+ }
+ },
+ "node_modules/@pagefind/darwin-arm64": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.5.2.tgz",
+ "integrity": "sha512-MXpI+7HsAdPkvJ0gk9xj9g541BCqBZOBbdwj9g6lB5LCj6kSV6nqDSjzcAJwvOsfu0fjwvC8hQU+ecfhp+MpiQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@pagefind/darwin-x64": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.5.2.tgz",
+ "integrity": "sha512-IojxFWMEJe0RQ7PQ3KXQsPIImNsbpPYpoZ+QUDrL8fAl/O27IX+LVLs74/UzEZy5uA2LD8Nz1AiwKr72vrkZQw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@pagefind/default-ui": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@pagefind/default-ui/-/default-ui-1.5.2.tgz",
+ "integrity": "sha512-pm1LMnQg8N2B3n2TnjKlhaFihpz6zTiA4HiGQ6/slKO/+8K9CAU5kcjdSSPgpuk1PMuuN4hxLipUIifnrkl3Sg==",
+ "license": "MIT"
+ },
+ "node_modules/@pagefind/freebsd-x64": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.5.2.tgz",
+ "integrity": "sha512-7EVzo9+0w+2cbe671BtMj10UlNo83I+HrLVLfRxO731svHRJKUfJ/mo05gU14pe9PCfpKNQT8FS3Xc/oDN6pOA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@pagefind/linux-arm64": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.5.2.tgz",
+ "integrity": "sha512-Ovt9+K35sqzn8H3ZMXGwls4TD/wMJuvRtShHIsmUQREmaxjrDEX7gHckRCrwYJ4XE1H1p6HkLz3wukrAnsfXQw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@pagefind/linux-x64": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.5.2.tgz",
+ "integrity": "sha512-V+tFqHKXhQKq/WqPBD67AFy7scn1/aZID00ws4fSDd+1daSi5UHR9VVlRrOUYKxn3VuFQYRD7lYXdZK1WED1YA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@pagefind/windows-arm64": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@pagefind/windows-arm64/-/windows-arm64-1.5.2.tgz",
+ "integrity": "sha512-hN9Nh90fNW61nNRCW9ZyQrAj/mD0eRvmJ8NlTUzkbuW8kIzGJUi3cxjFkEcMZ5h/8FsKWD/VcouZl4yo1F7B6g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@pagefind/windows-x64": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.5.2.tgz",
+ "integrity": "sha512-Fa2Iyw7kaDRzGMfNYNUXNW2zbL5FQVDgSOcbDHdzBrDEdpqOqg8TcZ68F22ol6NJ9IGzvUdmeyZypLW5dyhqsg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rolldown/binding-android-arm-eabi": {
+ "version": "1.2.15",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.15.tgz",
+ "integrity": "sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==",
+ "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.15",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.15.tgz",
+ "integrity": "sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg==",
+ "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.15",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.15.tgz",
+ "integrity": "sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA==",
+ "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.15",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.15.tgz",
+ "integrity": "sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A==",
+ "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.15",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.15.tgz",
+ "integrity": "sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ==",
+ "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.15",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.15.tgz",
+ "integrity": "sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ==",
+ "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.15",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.15.tgz",
+ "integrity": "sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.2.15",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.15.tgz",
+ "integrity": "sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.2.15",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.15.tgz",
+ "integrity": "sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.2.15",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.15.tgz",
+ "integrity": "sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==",
+ "cpu": [
+ "s390x"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.2.15",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.15.tgz",
+ "integrity": "sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.2.15",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.15.tgz",
+ "integrity": "sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.2.15",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.15.tgz",
+ "integrity": "sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==",
+ "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.15",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.15.tgz",
+ "integrity": "sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg==",
+ "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.15",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.15.tgz",
+ "integrity": "sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ==",
+ "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.1.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.1.0.tgz",
+ "integrity": "sha512-cVBuhDQjBZaNMTwBquYfPydrUP+E3hE6oYGasx8h6ZrODIM7/r+gxLDXdd66oszbrggZHEntdE6j+5gYIVyMWQ==",
+ "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==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/debug": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz",
+ "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/ms": "*"
+ }
+ },
+ "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/js-yaml": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz",
+ "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/mdast": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
+ "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/ms": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/nlcst": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz",
+ "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "24.19.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.19.2.tgz",
+ "integrity": "sha512-93d49wCWJkCyN87MxLpNN6KXPCwFw7bVUg79mGUV1OS40JGWwVCr8n61DXCSqz7CV6/VfPkb7xec7uP2M+QHWQ==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": ">=7.24.0 <7.24.7"
+ }
+ },
+ "node_modules/@types/sax": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz",
+ "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/unist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+ "license": "MIT"
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.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/am-i-vibing": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/am-i-vibing/-/am-i-vibing-0.5.0.tgz",
+ "integrity": "sha512-KLcsDhqf1XRrJmJr+9I/C0EX2K+sGrAGZFgkmd84c8TJO9zJBu+/kFInjazu1KfJWFhWDKdRWkpBzEs7s35DVA==",
+ "license": "MIT",
+ "dependencies": {
+ "process-ancestry": "^0.1.0"
+ },
+ "bin": {
+ "am-i-vibing": "dist/cli.mjs"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz",
+ "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==",
+ "license": "MIT",
+ "dependencies": {
+ "environment": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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/arg": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
+ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
+ "license": "MIT"
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "license": "Python-2.0"
+ },
+ "node_modules/aria-query": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
+ "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/astro": {
+ "version": "7.4.2",
+ "resolved": "https://registry.npmjs.org/astro/-/astro-7.4.2.tgz",
+ "integrity": "sha512-FAH4cdDlFaVnQGjazLTEIC5vJqGlo5XANc1wIbzQHIqAjJTll/RWTUs+SPNW1JdezG4YGxMZllaE5A41T2kzhA==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/compiler-rs": "^0.5.1",
+ "@astrojs/internal-helpers": "0.13.0",
+ "@astrojs/markdown-satteri": "0.4.5",
+ "@astrojs/telemetry": "3.4.1",
+ "@capsizecss/unpack": "^4.0.1",
+ "@clack/prompts": "^1.8.1",
+ "@oslojs/encoding": "^1.1.0",
+ "am-i-vibing": "^0.5.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": "^6.0.2",
+ "diff": "^9.0.0",
+ "dset": "^3.1.4",
+ "es-module-lexer": "^3.0.3",
+ "flattie": "^1.1.1",
+ "fontace": "~0.4.1",
+ "get-tsconfig": "5.0.0-beta.6",
+ "github-slugger": "^2.0.0",
+ "html-escaper": "3.0.3",
+ "http-cache-semantics": "^4.3.0",
+ "jsonc-parser": "^3.3.1",
+ "magic-string": "^1.4.3",
+ "magicast": "^0.5.5",
+ "mrmime": "^2.0.1",
+ "neotraverse": "^1.0.1",
+ "obug": "^3.0.0",
+ "p-limit": "^7.3.3",
+ "p-queue": "^9.3.5",
+ "package-manager-detector": "^1.9.0",
+ "piccolore": "^0.1.3",
+ "picomatch": "^4.0.7",
+ "rolldown": "^1.2.13",
+ "shiki": "^4.5.0",
+ "smol-toml": "^1.9.1",
+ "svgo": "^4.1.0",
+ "tinyclip": "^1.0.3",
+ "tinyexec": "^1.3.1",
+ "tinyglobby": "^0.2.17",
+ "tinyps": "^0.5.0",
+ "ultrahtml": "^1.7.0",
+ "unifont": "^1.0.3",
+ "unstorage": "^1.17.5",
+ "verkit": "^0.5.0",
+ "vite": "^8.3.4",
+ "vitefu": "^1.1.3",
+ "xxhash-wasm": "^1.1.0",
+ "yargs-parser": "^22.0.0",
+ "zod": "^4.6.6"
+ },
+ "bin": {
+ "astro": "bin/astro.mjs"
+ },
+ "engines": {
+ "node": ">=22.23.4",
+ "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/astro-expressive-code": {
+ "version": "0.44.3",
+ "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.44.3.tgz",
+ "integrity": "sha512-LUpQmmvzr90JXP5QIAopgdjlOKLuT3x0wlPAUpc4OZtPtFS8sbflPcGPpL/lG3pQuUCQV2CXUFgYNBYNu9tphA==",
+ "license": "MIT",
+ "dependencies": {
+ "rehype-expressive-code": "^0.44.3",
+ "url-extras": "^0.1.0"
+ },
+ "peerDependencies": {
+ "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta || ^7.0.0"
+ }
+ },
+ "node_modules/axobject-query": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
+ "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/bail": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
+ "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/bcp-47": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-2.1.2.tgz",
+ "integrity": "sha512-wsv86UuNFuPnVieKLyECTT+CP1WSAHeoIg5P3xu2kA9Z+7fVPoKt7dxzSeEhtJel0AI+NW3fIW3KtTKQKUCngg==",
+ "license": "MIT",
+ "dependencies": {
+ "is-alphabetical": "^2.0.0",
+ "is-alphanumerical": "^2.0.0",
+ "is-decimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/bcp-47-match": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz",
+ "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/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": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
+ "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-html4": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
+ "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-legacy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-reference-invalid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
+ "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "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/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/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-selector-parser": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.3.0.tgz",
+ "integrity": "sha512-Y2asgMGFqJKF4fq4xHDSlFYIkeVfRsm69lQC1q9kbEsH5XtnINTMrweLkjYMeaUgiXBy/uvKeO/a1JHTNnmB2g==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/mdevils"
+ },
+ {
+ "type": "patreon",
+ "url": "https://patreon.com/mdevils"
+ }
+ ],
+ "license": "MIT"
+ },
+ "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/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "license": "MIT",
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/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/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decode-named-character-reference": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz",
+ "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "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": "6.0.3",
+ "resolved": "https://registry.npmjs.org/devalue/-/devalue-6.0.3.tgz",
+ "integrity": "sha512-3woMEITftG1wKeO/4d/QXH1DVKctTSZg3jNGPpjp9hNr5Hos8MCFF5McTML5ZtgKDObm2R591B0spRp2IVernA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "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/direction": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz",
+ "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==",
+ "license": "MIT",
+ "bin": {
+ "direction": "cli.js"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/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/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/environment": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+ "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-3.0.4.tgz",
+ "integrity": "sha512-BldNY5mwkVu+cQ5Ymn3e6hB9Sx8XHx4ZevWbiUyZbfKxJxYO27Hf0wfzkxIaD5CAJMaGl3YspKELj0ff/7nCMA==",
+ "license": "MIT"
+ },
+ "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/expressive-code": {
+ "version": "0.44.3",
+ "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.44.3.tgz",
+ "integrity": "sha512-7RGslhJTNt01pdn5I3QmVh5C8ZIiSm0A+ORQ9j9r39oOgJVKjn/7dXl9rMT/SqmFLE6pJkTYI7UjW0BxJnW7OQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@expressive-code/core": "^0.44.3",
+ "@expressive-code/plugin-frames": "^0.44.3",
+ "@expressive-code/plugin-shiki": "^0.44.3",
+ "@expressive-code/plugin-text-markers": "^0.44.3"
+ }
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "license": "MIT"
+ },
+ "node_modules/fast-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-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/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/fnv1a-64": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/fnv1a-64/-/fnv1a-64-0.1.2.tgz",
+ "integrity": "sha512-mJbcILTPybAR1jdOwt/lVv8N2cffeJFFP+gVbSAwLEx1ujXH27RpPd8Rokec/KX9c76UYIB6Co+H4lQzxPnJfA==",
+ "license": "MIT"
+ },
+ "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-tsconfig": {
+ "version": "5.0.0-beta.6",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-5.0.0-beta.6.tgz",
+ "integrity": "sha512-X6fBC0pmImC70gvX2zm56go9hx0MyoGVdG0tUCkg/D+Xnh5TJsOZ7iDbOdI3PvmtrDxnu1YdDufpK2QJX1Meqw==",
+ "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/has-flag": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-5.0.1.tgz",
+ "integrity": "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/hast-util-embedded": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz",
+ "integrity": "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-is-element": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-format": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/hast-util-format/-/hast-util-format-1.1.0.tgz",
+ "integrity": "sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-embedded": "^3.0.0",
+ "hast-util-minify-whitespace": "^1.0.0",
+ "hast-util-phrasing": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "html-whitespace-sensitive-tag-names": "^3.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-from-html": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz",
+ "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "devlop": "^1.1.0",
+ "hast-util-from-parse5": "^8.0.0",
+ "parse5": "^7.0.0",
+ "vfile": "^6.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-from-parse5": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz",
+ "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "devlop": "^1.0.0",
+ "hastscript": "^9.0.0",
+ "property-information": "^7.0.0",
+ "vfile": "^6.0.0",
+ "vfile-location": "^5.0.0",
+ "web-namespaces": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-has-property": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz",
+ "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-is-body-ok-link": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-is-body-ok-link/-/hast-util-is-body-ok-link-3.0.1.tgz",
+ "integrity": "sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-is-element": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz",
+ "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-minify-whitespace": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-minify-whitespace/-/hast-util-minify-whitespace-1.0.1.tgz",
+ "integrity": "sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-embedded": "^3.0.0",
+ "hast-util-is-element": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-parse-selector": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz",
+ "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-phrasing": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-phrasing/-/hast-util-phrasing-3.0.1.tgz",
+ "integrity": "sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-embedded": "^3.0.0",
+ "hast-util-has-property": "^3.0.0",
+ "hast-util-is-body-ok-link": "^3.0.0",
+ "hast-util-is-element": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-select": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.4.tgz",
+ "integrity": "sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "bcp-47-match": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "css-selector-parser": "^3.0.0",
+ "devlop": "^1.0.0",
+ "direction": "^2.0.0",
+ "hast-util-has-property": "^3.0.0",
+ "hast-util-to-string": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "nth-check": "^2.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "unist-util-visit": "^5.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-html": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz",
+ "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "html-void-elements": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "stringify-entities": "^4.0.0",
+ "zwitch": "^2.0.4"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-string": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz",
+ "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-text": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz",
+ "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "hast-util-is-element": "^3.0.0",
+ "unist-util-find-after": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-whitespace": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
+ "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hastscript": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz",
+ "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-parse-selector": "^4.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/html-escaper": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz",
+ "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==",
+ "license": "MIT"
+ },
+ "node_modules/html-void-elements": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz",
+ "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/html-whitespace-sensitive-tag-names": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/html-whitespace-sensitive-tag-names/-/html-whitespace-sensitive-tag-names-3.0.1.tgz",
+ "integrity": "sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/http-cache-semantics": {
+ "version": "4.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/i18next": {
+ "version": "26.5.5",
+ "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.5.5.tgz",
+ "integrity": "sha512-txdaNOlnZCdiTMSlEzl5hUJU8TC9R4J/RCR1G7XrDPbWT2t3mR142QPz84ZuZeXDt3yq+9zmsdPiakxYtmp3fw==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://www.locize.com/i18next"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.locize.com"
+ }
+ ],
+ "license": "MIT",
+ "peerDependencies": {
+ "typescript": "^5 || ^6 || ^7"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "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-absolute-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-5.0.0.tgz",
+ "integrity": "sha512-sdJyNpBnQHuVnBunfzjAecOhZr2+A30ywfFvu3EnxtKLUWfwGgyWUmqHbGZiU6vTfHpCPm5GvLe4BAvlU9n8VQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-alphabetical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
+ "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-alphanumerical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
+ "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
+ "license": "MIT",
+ "dependencies": {
+ "is-alphabetical": "^2.0.0",
+ "is-decimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-decimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
+ "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-docker": {
+ "version": "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-hexadecimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
+ "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-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/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/klona": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz",
+ "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/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"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "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"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "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"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "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"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "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/longest-streak": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
+ "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "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-directive": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.1.1.tgz",
+ "integrity": "sha512-Gqpa3MHorXYEZ0VfbroyvBlVKUfz371V6mIERqOYMcb0W1D3LwmFq1ZW4O2TDrbn+4ZmGLwMWeFRJgYZ+bc5fQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "parse-entities": "^4.0.0",
+ "stringify-entities": "^4.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-from-markdown": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.1.0.tgz",
+ "integrity": "sha512-KRLgmPaP2H1ul2bpoJkOS+9pAcDAAj9Nu1mqLnh8NLeJG5tlvj42TH80PIGBpROWlRXLk0lDrWNuZCFy/tsB4A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark": "^4.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx-jsx": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz",
+ "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "parse-entities": "^4.0.0",
+ "stringify-entities": "^4.0.0",
+ "unist-util-stringify-position": "^4.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-phrasing": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
+ "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-hast": {
+ "version": "13.2.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/mdast-util-to-markdown": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.2.0.tgz",
+ "integrity": "sha512-2Jn/ADrHFFyyniBMi0bncTbJqU78K3/TEYvzvwrWWzD9MYPUa/Gm4r3nOeKFQg/PDM5BsbDYdc8ackW4NTY6NA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "longest-streak": "^3.0.0",
+ "mdast-util-phrasing": "^4.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "micromark-util-html-tag-name": "^2.0.0",
+ "unist-util-visit": "^5.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
+ "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdn-data": {
+ "version": "2.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": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.3.tgz",
+ "integrity": "sha512-oGYfQzHSG5dOMovQcJ3fyTmZlWAWpi0XA0sJwJs+i6OT88o1+Jtw/8z0CmdowSLxGhhFK89rQ/oXph/wN02PNw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/debug": "^4.0.0",
+ "debug": "^4.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-edit-map": "^1.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-core-commonmark": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.4.tgz",
+ "integrity": "sha512-wxEeE8v8XVvOrxn1TZj74qYhAtTQsSqufHVS3uNVyT1MupXxhyaHk8/9xDNPh9BmL77QNDgWTkZ3RsYHB2ry7w==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-destination": "^2.0.0",
+ "micromark-factory-label": "^2.0.0",
+ "micromark-factory-space": "^2.1.0",
+ "micromark-factory-title": "^2.0.0",
+ "micromark-factory-whitespace": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-edit-map": "^1.0.0",
+ "micromark-util-html-tag-name": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.3"
+ }
+ },
+ "node_modules/micromark-extension-directive": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz",
+ "integrity": "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-factory-whitespace": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "parse-entities": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-factory-destination": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
+ "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-label": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz",
+ "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-space": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.1.0.tgz",
+ "integrity": "sha512-fS8hnLIjnjvdQIj39Geug8wWsR0HrYZ43KShKxNfwT7t2LOHo/LbWZEzEaSOjwtjdCsjoE6syHhonEzW0zv0+Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-title": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz",
+ "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-whitespace": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz",
+ "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-chunked": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz",
+ "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-classify-character": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz",
+ "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-combine-extensions": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz",
+ "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-numeric-character-reference": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz",
+ "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-string": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz",
+ "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-edit-map": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-edit-map/-/micromark-util-edit-map-1.0.0.tgz",
+ "integrity": "sha512-Pa2ljlsEL6sVwFaYeyrOLSYbQt73JvGbPOYFq+9AElXiSnfI5Q4465RRZ1sHv7lDjDOnGRDDaqPXqClqWK/q1Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-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-html-tag-name": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz",
+ "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-normalize-identifier": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz",
+ "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-resolve-all": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz",
+ "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-sanitize-uri": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
+ "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-subtokenize": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz",
+ "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-types": {
+ "version": "2.0.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/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.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/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.5",
+ "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.5.tgz",
+ "integrity": "sha512-Lki/zhXFVmDhesBUn/yHRLW9Bte7mE7xoFHzTR8sIomV7a2z+LxYAPkGvdfidNiu4fKSfjtawixxSHcZci5QcQ==",
+ "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/pagefind": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.5.2.tgz",
+ "integrity": "sha512-XTUaK0hXMCu2jszWE584JGQT7y284TmMV9l/HX3rnG5uo3rHI/uHU56XTyyyPFjeWEBxECbAi0CaFDJOONtG0Q==",
+ "license": "MIT",
+ "bin": {
+ "pagefind": "lib/runner/bin.cjs"
+ },
+ "optionalDependencies": {
+ "@pagefind/darwin-arm64": "1.5.2",
+ "@pagefind/darwin-x64": "1.5.2",
+ "@pagefind/freebsd-x64": "1.5.2",
+ "@pagefind/linux-arm64": "1.5.2",
+ "@pagefind/linux-x64": "1.5.2",
+ "@pagefind/windows-arm64": "1.5.2",
+ "@pagefind/windows-x64": "1.5.2"
+ }
+ },
+ "node_modules/parse-entities": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz",
+ "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "character-entities-legacy": "^3.0.0",
+ "character-reference-invalid": "^2.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "is-alphanumerical": "^2.0.0",
+ "is-decimal": "^2.0.0",
+ "is-hexadecimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/parse-entities/node_modules/@types/unist": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+ "license": "MIT"
+ },
+ "node_modules/parse5": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
+ "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+ "license": "MIT",
+ "dependencies": {
+ "entities": "^6.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/parse5/node_modules/entities": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/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/postcss": {
+ "version": "8.5.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz",
+ "integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==",
+ "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.20",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-nested": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-7.0.2.tgz",
+ "integrity": "sha512-5osppouFc0VR9/VYzYxO03VaDa3e8F23Kfd6/9qcZTUI8P58GIYlArOET2Wq0ywSl2o2PjELhYOFI4W7l5QHKw==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.14"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "7.1.7",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.7.tgz",
+ "integrity": "sha512-wWBro/7hJ40ye6TPiXkUQoXKCw23xSsjriUvqd7Uls9Sso3oLiON1flc18cCdnjgCRXw4fBVqUJJeOdDTh3P3g==",
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/prismjs": {
+ "version": "1.30.0",
+ "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
+ "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/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": "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/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/rehype-expressive-code": {
+ "version": "0.44.3",
+ "resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.44.3.tgz",
+ "integrity": "sha512-vaVuQPobFXvmtcHXQ5uqXZlzuuPnLyIuZcX/03sX1U/Qfj9fZrPYI9w66OSr/PUx6alzRYmreEwgYBbsVx8HsA==",
+ "license": "MIT",
+ "dependencies": {
+ "expressive-code": "^0.44.3"
+ }
+ },
+ "node_modules/remark-directive": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-4.0.0.tgz",
+ "integrity": "sha512-7sxn4RfF1o3izevPV1DheyGDD6X4c9hrGpfdUpm7uC++dqrnJxIZVkk7CoKqcLm0VUMAuOol7Mno3m6g8cfMuA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-directive": "^3.0.0",
+ "micromark-extension-directive": "^4.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "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.15",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.15.tgz",
+ "integrity": "sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g==",
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/types": "=0.155.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.15",
+ "@rolldown/binding-android-arm64": "1.2.15",
+ "@rolldown/binding-darwin-arm64": "1.2.15",
+ "@rolldown/binding-darwin-x64": "1.2.15",
+ "@rolldown/binding-freebsd-x64": "1.2.15",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.2.15",
+ "@rolldown/binding-linux-arm64-gnu": "1.2.15",
+ "@rolldown/binding-linux-arm64-musl": "1.2.15",
+ "@rolldown/binding-linux-ppc64-gnu": "1.2.15",
+ "@rolldown/binding-linux-s390x-gnu": "1.2.15",
+ "@rolldown/binding-linux-x64-gnu": "1.2.15",
+ "@rolldown/binding-linux-x64-musl": "1.2.15",
+ "@rolldown/binding-openharmony-arm64": "1.2.15",
+ "@rolldown/binding-win32-arm64-msvc": "1.2.15",
+ "@rolldown/binding-win32-x64-msvc": "1.2.15"
+ }
+ },
+ "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==",
+ "license": "ISC",
+ "optional": true,
+ "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/sitemap": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-9.0.1.tgz",
+ "integrity": "sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "^24.9.2",
+ "@types/sax": "^1.2.1",
+ "arg": "^5.0.0",
+ "sax": "^1.4.1"
+ },
+ "bin": {
+ "sitemap": "dist/esm/cli.js"
+ },
+ "engines": {
+ "node": ">=20.19.5",
+ "npm": ">=10.8.2"
+ }
+ },
+ "node_modules/smol-toml": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.9.1.tgz",
+ "integrity": "sha512-j7WyPdf0CJoutMzMoPLF/bVXHtKZIT9Rb4eGeEbWw8X2t5oirjPgpL+OpaENZrILYaXqYwnVH+qgUwVbl+pUJQ==",
+ "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/starlight-links-validator": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/starlight-links-validator/-/starlight-links-validator-0.27.0.tgz",
+ "integrity": "sha512-UvPl8L9hfWR+yWbLOEtljLmpJCGcM6AVseVy5IO9dmqdOjW4R+Y8FaYyVA5axCC8RvxM5xU7M1WTerpwU8rYbw==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/markdown-satteri": "^0.4.0",
+ "github-slugger": "^2.0.0",
+ "hast-util-from-html": "^2.0.3",
+ "is-absolute-url": "^5.0.0",
+ "mdast-util-mdx-jsx": "^3.2.0",
+ "mdast-util-to-hast": "^13.2.1",
+ "picomatch": "^4.0.3",
+ "satteri": "^0.10.3",
+ "terminal-link": "^5.0.0",
+ "unist-util-visit": "^5.1.0",
+ "yaml": "^2.8.3"
+ },
+ "peerDependencies": {
+ "@astrojs/starlight": ">=0.42.0",
+ "astro": ">=7.2.10"
+ }
+ },
+ "node_modules/starlight-sidebar-topics": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/starlight-sidebar-topics/-/starlight-sidebar-topics-0.9.0.tgz",
+ "integrity": "sha512-xFwMAAtTVyqsO5DZ8Ldk0vN+2QoPdtjtDi8fbrzvQATIJvIvfaBLL9JzfXAjNHI9vMkj35VMWMd8tTICeO6RIA==",
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=22.12.0"
+ },
+ "peerDependencies": {
+ "@astrojs/starlight": ">=0.42.0",
+ "astro": "^7.2.10"
+ }
+ },
+ "node_modules/std-env": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.3.0.tgz",
+ "integrity": "sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==",
+ "license": "MIT"
+ },
+ "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/supports-color": {
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz",
+ "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/supports-hyperlinks": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-4.6.0.tgz",
+ "integrity": "sha512-FL/q/YePS4tFFTNiBOR+zPk2ZrKheqNtmTJ/9OUZ4e1wKHJofDbGQSmSsxS0XNnabSkqexryCaXGvEKz1vf9UQ==",
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^5.0.1",
+ "supports-color": "^10.2.2"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1"
+ }
+ },
+ "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/terminal-link": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-5.0.0.tgz",
+ "integrity": "sha512-qFAy10MTMwjzjU8U16YS4YoZD+NQLHzLssFMNqgravjbvIPNiqkGFR4yjhJfmY9R5OFU7+yHxc6y+uGHkKwLRA==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-escapes": "^7.0.0",
+ "supports-hyperlinks": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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/tinyps": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/tinyps/-/tinyps-0.5.1.tgz",
+ "integrity": "sha512-B6Ykif5HflC3AdDkS4Zst/2muYVfeXoVMy+KorqH80s/1x4vunIORpNySH681X7fjj+dFM9MDKD6/Hsccjb87Q==",
+ "license": "MIT"
+ },
+ "node_modules/trim-lines": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
+ "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/trough": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
+ "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD",
+ "optional": true
+ },
+ "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.12.3",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-8.12.3.tgz",
+ "integrity": "sha512-+fdndUTwBP86cVjihTp21hZHo7d6oj1c/47XPA3lL8mM0XHs+C4EkcjmgRsFnFk2f/UONGDy9ItBItmXsLOQHw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=22.19.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.24.6",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
+ "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
+ "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": "1.0.3",
+ "resolved": "https://registry.npmjs.org/unifont/-/unifont-1.0.3.tgz",
+ "integrity": "sha512-XGnEwj5sWdCbiBX01mWLSBncS0yMRjnLygau3IfdOlfjMacNIQvTGW4VH7pWBq+gGWArfSef/CensMLjMmIr/A==",
+ "license": "MIT",
+ "dependencies": {
+ "css-tree": "^3.1.0",
+ "fnv1a-64": "^0.1.2",
+ "std-env": "^4.2.0",
+ "undici": "^8.0.0"
+ }
+ },
+ "node_modules/unist-util-find-after": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz",
+ "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-is": {
+ "version": "6.0.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.6",
+ "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.6.tgz",
+ "integrity": "sha512-gjKtcmn/KwZuJZO9o2sc0esAQnhmTw0LviDHwevTXbJMYs0dRTp8ZEEWXHU0vTpzbN3cA0yxlTeyXssMp3zDqw==",
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "^3.1.3",
+ "chokidar": "^5.0.0",
+ "destr": "^2.0.5",
+ "h3": "^1.15.11",
+ "lru-cache": "^11.5.3",
+ "node-fetch-native": "^1.6.7",
+ "ofetch": "^1.5.1",
+ "ufo": "^1.6.4"
+ },
+ "peerDependencies": {
+ "@azure/app-configuration": "^1.8.0",
+ "@azure/cosmos": "^4.2.0",
+ "@azure/data-tables": "^13.3.0",
+ "@azure/identity": "^4.6.0",
+ "@azure/keyvault-secrets": "^4.9.0",
+ "@azure/storage-blob": "^12.26.0",
+ "@capacitor/preferences": "^6 || ^7 || ^8",
+ "@deno/kv": ">=0.9.0",
+ "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 || ^11.0.0",
+ "@planetscale/database": "^1.19.0",
+ "@upstash/redis": "^1.34.3",
+ "@vercel/blob": ">=0.27.1",
+ "@vercel/functions": "^2.2.12 || ^3.0.0",
+ "@vercel/kv": "^1 || ^2 || ^3",
+ "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/url-extras": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/url-extras/-/url-extras-0.1.0.tgz",
+ "integrity": "sha512-8tzwTeXFPuX/5PHuCDQE5Dd9Ts4rwoq2t9aIT+HS4iAVpmj5l4Ao7Q+BuuFjvWRqrLswBhQDk8O96ZicgCqQqw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "license": "MIT"
+ },
+ "node_modules/verkit": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/verkit/-/verkit-0.5.0.tgz",
+ "integrity": "sha512-u4J1hr6Y+C/hit34GwL3eIcUf3wngT8TlYUjK7tk0jxydKLAq3sYvifjdTDA6U3KCKRcQCQDGf/srjxfZF3l7g==",
+ "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-location": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz",
+ "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-message": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz",
+ "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vite": {
+ "version": "8.3.5",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.5.tgz",
+ "integrity": "sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ==",
+ "license": "MIT",
+ "dependencies": {
+ "lightningcss": "^1.33.0",
+ "picomatch": "^4.0.7",
+ "postcss": "^8.5.30",
+ "rolldown": "~1.2.14",
+ "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 || ^0.8.0",
+ "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/web-namespaces": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
+ "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/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/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/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.7",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.7.tgz",
+ "integrity": "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==",
+ "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 00000000..83f39b96
--- /dev/null
+++ b/website/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "retrobios-site",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "build": "astro build",
+ "preview": "astro preview"
+ },
+ "dependencies": {
+ "@astrojs/markdown-satteri": "0.4.5",
+ "@astrojs/sitemap": "3.7.4",
+ "@astrojs/starlight": "0.42.6",
+ "astro": "7.4.2",
+ "starlight-links-validator": "0.27.0",
+ "starlight-sidebar-topics": "0.9.0"
+ }
+}
diff --git a/website/src/assets/logo-dark.svg b/website/src/assets/logo-dark.svg
new file mode 100644
index 00000000..31174ea3
--- /dev/null
+++ b/website/src/assets/logo-dark.svg
@@ -0,0 +1 @@
+
diff --git a/website/src/assets/logo-light.svg b/website/src/assets/logo-light.svg
new file mode 100644
index 00000000..88b79ee3
--- /dev/null
+++ b/website/src/assets/logo-light.svg
@@ -0,0 +1 @@
+
diff --git a/website/src/components/FilePicker.astro b/website/src/components/FilePicker.astro
new file mode 100644
index 00000000..793e8e5e
--- /dev/null
+++ b/website/src/components/FilePicker.astro
@@ -0,0 +1,186 @@
+---
+// "Which files do I need": pick a setup and systems, get the file list and
+// the install command. Client side only: reads public/data/picker/*.json,
+// which convert.py derives from install/.json. The selection
+// follows install.py narrow(): a file is kept when one of its systems is
+// chosen, a file with no system when "other" is chosen.
+const base = import.meta.env.BASE_URL.replace(/\/?$/, '/');
+---
+
+
+
+ Your setup
+
+ Choose a setup
+
+
+
+
+ Systems
+ All
+ None
+
+
+
+
+
+
+
Install command (Linux, macOS, Steam Deck, handhelds)
+
+
Copy the command
+
+ Files and where they go
+
+
+
+
+
+
+
+
+
diff --git a/website/src/components/Footer.astro b/website/src/components/Footer.astro
new file mode 100644
index 00000000..4ad1c9f5
--- /dev/null
+++ b/website/src/components/Footer.astro
@@ -0,0 +1,10 @@
+---
+// Starlight's footer (edit link, previous and next) and the licence line.
+import Default from '@astrojs/starlight/components/Footer.astro';
+---
+
+
+
+ MIT for the tooling. BIOS and firmware files are third-party system software, preserved for personal backup,
+ archival and interoperability.
+
diff --git a/website/src/components/MarkdownContent.astro b/website/src/components/MarkdownContent.astro
new file mode 100644
index 00000000..44cb19e3
--- /dev/null
+++ b/website/src/components/MarkdownContent.astro
@@ -0,0 +1,21 @@
+---
+// Search ranking: Pagefind multiplies the score of words inside a weighted
+// element, so a Start here page outranks the reference page that names it.
+import Default from '@astrojs/starlight/components/MarkdownContent.astro';
+
+const { boost } = Astro.locals.starlightRoute.entry.data;
+---
+
+{
+ boost ? (
+
+
+
+ ) : (
+
+ )
+}
+
+
diff --git a/website/src/components/PageTitle.astro b/website/src/components/PageTitle.astro
new file mode 100644
index 00000000..526a780f
--- /dev/null
+++ b/website/src/components/PageTitle.astro
@@ -0,0 +1,40 @@
+---
+// Breadcrumbs, the level label and the title. The trail says which tab a
+// deep page belongs to; the label says who the page is written for.
+const PAGE_TITLE_ID = '_top';
+
+const LABELS = {
+ start: 'Start here',
+ setup: 'Setup guide',
+ explore: 'Details',
+ reference: 'Reference',
+ contribute: 'For contributors',
+} as const;
+
+const { title, level } = Astro.locals.starlightRoute.entry.data;
+const crumbs = Astro.locals.rbBreadcrumbs ?? [];
+---
+
+{
+ crumbs.length > 0 && (
+
+
+ {crumbs.map((crumb) => (
+ {crumb.href ? {crumb.label} : crumb.label}
+ ))}
+
+
+ )
+}
+{level && {LABELS[level]} }
+{title}
+
+
diff --git a/website/src/content.config.ts b/website/src/content.config.ts
new file mode 100644
index 00000000..15a91e1b
--- /dev/null
+++ b/website/src/content.config.ts
@@ -0,0 +1,22 @@
+import { defineCollection } from 'astro:content';
+import { z } from 'astro/zod';
+import { docsLoader } from '@astrojs/starlight/loaders';
+import { docsSchema } from '@astrojs/starlight/schema';
+
+// The generator's own front matter, typed: a page with a misspelt level fails the build.
+export const collections = {
+ docs: defineCollection({
+ loader: docsLoader(),
+ schema: docsSchema({
+ extend: z.object({
+ // Who the page is written for, from someone who knows nothing
+ // (start) to someone adding to the project (contribute).
+ level: z.enum(['start', 'setup', 'explore', 'reference', 'contribute']).optional(),
+ // Search ranking of the first two levels.
+ boost: z.number().min(1).max(10).optional(),
+ // The date the page's content carries, for the sitemap.
+ lastmod: z.string().optional(),
+ }),
+ }),
+ }),
+};
diff --git a/website/src/env.d.ts b/website/src/env.d.ts
new file mode 100644
index 00000000..15399ae0
--- /dev/null
+++ b/website/src/env.d.ts
@@ -0,0 +1,7 @@
+///
+
+declare namespace App {
+ interface Locals {
+ rbBreadcrumbs?: import('./routeData').Crumb[];
+ }
+}
diff --git a/website/src/routeData.ts b/website/src/routeData.ts
new file mode 100644
index 00000000..b9f63a27
--- /dev/null
+++ b/website/src/routeData.ts
@@ -0,0 +1,96 @@
+// What each page knows of its place in the site, computed once per page:
+// - breadcrumbs: the tab and the groups above the page;
+// - previous and next: the reading order runs on from one tab into the next,
+// so the last Start here page leads to Your setup;
+// - a pruned sidebar: a group that does not hold the page becomes one link
+// to its first page, so an emulator page does not carry the 600 links of
+// every other emulator (the equivalent of Material's navigation.prune).
+import { defineRouteMiddleware, type StarlightRouteData } from '@astrojs/starlight/route-data';
+import sidebar from './generated/sidebar.json' with { type: 'json' };
+
+type Entry = StarlightRouteData['sidebar'][number];
+type Link = Extract;
+type Item = { label: string; link?: string; items?: Item[] };
+export type Crumb = { label: string; href?: string };
+
+const BASE = import.meta.env.BASE_URL.replace(/\/?$/, '/');
+const withBase = (link: string) => (link.startsWith(BASE) ? link : BASE + link.replace(/^\//, ''));
+
+const flatten = (items: Item[]): { label: string; link: string }[] =>
+ items.flatMap((item) => (item.items ? flatten(item.items) : item.link && !item.link.startsWith('http') ? [{ label: item.label, link: item.link }] : []));
+
+// Every listed page in reading order, tab after tab.
+const READING_ORDER = sidebar.topics.flatMap((topic) =>
+ flatten(topic.items as Item[]).map((entry) => ({ ...entry, label: entry.label === 'Overview' ? topic.label : entry.label })),
+);
+
+const holdsCurrent = (entry: Entry): boolean =>
+ entry.type === 'link' ? entry.isCurrent : entry.entries.some(holdsCurrent);
+
+const firstLink = (entry: Entry): string | undefined =>
+ entry.type === 'link' ? entry.href : entry.entries.map(firstLink).find(Boolean);
+
+const prune = (entries: Entry[]): Entry[] =>
+ entries.map((entry) => {
+ if (entry.type === 'link') return entry;
+ if (holdsCurrent(entry)) return { ...entry, collapsed: false, entries: prune(entry.entries) };
+ const href = firstLink(entry);
+ return href ? { type: 'link', label: entry.label, href, isCurrent: false, badge: entry.badge, attrs: {} } : entry;
+ });
+
+// The groups above the current page, outermost first.
+const trail = (entries: Entry[], path: string): Crumb[] | undefined => {
+ for (const entry of entries) {
+ if (entry.type === 'link') {
+ if (entry.isCurrent) return [];
+ continue;
+ }
+ const below = trail(entry.entries, path);
+ if (below) return [{ label: entry.label, href: firstLink(entry) }, ...below];
+ }
+ return undefined;
+};
+
+// A page the navigation does not list (part of a split page) sits under the
+// group holding the pages of its folder.
+const folderTrail = (entries: Entry[], folder: string): Crumb[] => {
+ for (const entry of entries) {
+ if (entry.type !== 'group') continue;
+ const links = entry.entries.map(firstLink).filter(Boolean) as string[];
+ if (links.some((href) => href.startsWith(folder))) return [{ label: entry.label, href: links[0] }];
+ }
+ return [];
+};
+
+const asLink = (entry: { label: string; link: string }): Link => ({
+ type: 'link',
+ label: entry.label,
+ href: withBase(entry.link),
+ isCurrent: false,
+ badge: undefined,
+ attrs: {},
+});
+
+export const onRequest = defineRouteMiddleware(async (context, next) => {
+ await next();
+ const route = context.locals.starlightRoute;
+ const path = context.url.pathname;
+
+ const topic = context.locals.starlightSidebarTopics?.topics.find((t) => t.isCurrent);
+ const crumbs: Crumb[] = [];
+ if (topic && withBase(topic.link) !== path) {
+ crumbs.push({ label: topic.label, href: withBase(topic.link) });
+ const segments = path.slice(BASE.length).split('/').filter(Boolean);
+ const groups = trail(route.sidebar, path) ?? (segments.length > 1 ? folderTrail(route.sidebar, `${BASE}${segments[0]}/`) : []);
+ crumbs.push(...groups.filter((crumb) => crumb.href !== path));
+ }
+ context.locals.rbBreadcrumbs = crumbs;
+
+ const position = READING_ORDER.findIndex((entry) => withBase(entry.link) === path);
+ if (position !== -1) {
+ if (!route.pagination.prev && position > 0) route.pagination.prev = asLink(READING_ORDER[position - 1]);
+ if (!route.pagination.next && position < READING_ORDER.length - 1) route.pagination.next = asLink(READING_ORDER[position + 1]);
+ }
+
+ route.sidebar = prune(route.sidebar);
+});
diff --git a/website/src/scripts/site.ts b/website/src/scripts/site.ts
new file mode 100644
index 00000000..9d9bfe77
--- /dev/null
+++ b/website/src/scripts/site.ts
@@ -0,0 +1,275 @@
+// Progressive enhancement of the generated pages: table captions, filters
+// and phone layout, the hash lookup on files/, moved cards, a hash typed in
+// the search, glossary terms on a tap, and the 404 page's hints.
+
+const HASH_LENGTHS: Record = { 8: 'CRC32', 32: 'MD5', 40: 'SHA1', 64: 'SHA256' };
+const LAYER_LABELS: Record = {
+ collection: 'In the collection',
+ 'data-directory': 'In a data directory the packs ship',
+ platform: 'Declared by a platform',
+ emulator: 'Read by an emulator',
+};
+type Item = { algorithm: string; layer: string; name?: string; value: string; url: string; owner?: string };
+
+const siteRoot = () => new URL(import.meta.env.BASE_URL.replace(/\/?$/, '/'), location.origin);
+const normalizeHash = (raw: string) => raw.trim().toLowerCase().replace(/^0x/, '').replace(/\s+/g, '');
+const isHash = (value: string) => /^[0-9a-f]+$/.test(value) && value.length in HASH_LENGTHS;
+
+// Tables
+
+const headingText = (element: Element): string => {
+ let cursor: Element | null = element;
+ while (cursor) {
+ cursor = cursor.previousElementSibling;
+ if (!cursor) break;
+ const heading = /^H[1-6]$/.test(cursor.tagName) ? cursor : cursor.querySelector('h1, h2, h3, h4, h5, h6');
+ if (heading && cursor.classList.contains('sl-heading-wrapper')) return heading.textContent!.trim();
+ if (/^H[1-6]$/.test(cursor.tagName)) return cursor.textContent!.trim();
+ }
+ return document.querySelector('h1')?.textContent?.trim() || 'Data table';
+};
+
+// A table that overflows is a named stop in the tab order, so a keyboard
+// can scroll it (WCAG 2.1.1). Checked again when a closed block opens.
+const markScrollable = (table: HTMLTableElement) => {
+ const heading = table.dataset.rbHeading ?? 'Data table';
+ if (table.scrollWidth > table.clientWidth + 1) {
+ table.tabIndex = 0;
+ table.setAttribute('role', 'region');
+ table.setAttribute('aria-label', `${heading}, scrollable table`);
+ } else if (table.getAttribute('role') === 'region') {
+ table.removeAttribute('tabindex');
+ table.removeAttribute('role');
+ table.removeAttribute('aria-label');
+ }
+};
+
+// A table whose rows end in an action (a download button) is laid out one
+// block per row on a phone, each cell under its column name, so the action
+// never sits behind a horizontal scroll.
+const stackActionTable = (table: HTMLTableElement) => {
+ if (!table.querySelector('tbody .rb-button')) return;
+ const labels = [...table.querySelectorAll('thead th')].map((cell) => cell.textContent!.trim());
+ if (!labels.length) return;
+ table.dataset.rbStack = 'true';
+ for (const row of table.querySelectorAll('tbody tr')) {
+ [...row.children].forEach((cell, column) => {
+ if (labels[column] && !cell.hasAttribute('data-label')) cell.setAttribute('data-label', labels[column]);
+ });
+ }
+};
+
+const filterTable = (table: HTMLTableElement, index: number, heading: string) => {
+ const rows = [...table.querySelectorAll('tbody tr')];
+ if (rows.length < 20) return;
+ const controls = document.createElement('div');
+ controls.className = 'rb-table-filter';
+ const id = `rb-table-filter-${index}`;
+ const label = document.createElement('label');
+ label.htmlFor = id;
+ label.textContent = `Filter ${heading}`;
+ const input = document.createElement('input');
+ input.id = id;
+ input.type = 'search';
+ input.autocomplete = 'off';
+ input.placeholder = 'Name, platform, system, hash...';
+ const count = document.createElement('span');
+ count.className = 'rb-table-filter-count';
+ count.setAttribute('aria-live', 'polite');
+ count.textContent = `${rows.length} rows`;
+ controls.append(label, input, count);
+ table.before(controls);
+ const searchable = rows.map((row) => ({ row, text: row.textContent!.normalize('NFKD').toLocaleLowerCase() }));
+ input.addEventListener('input', () => {
+ const query = input.value.trim().normalize('NFKD').toLocaleLowerCase();
+ let visible = 0;
+ for (const entry of searchable) {
+ entry.row.hidden = !!query && !entry.text.includes(query);
+ if (!entry.row.hidden) visible += 1;
+ }
+ count.textContent = `${visible} of ${rows.length} rows`;
+ });
+};
+
+const enhanceTables = () => {
+ document.querySelectorAll('.sl-markdown-content table').forEach((table, index) => {
+ for (const cell of table.querySelectorAll('thead th')) if (!cell.hasAttribute('scope')) cell.setAttribute('scope', 'col');
+ const heading = headingText(table);
+ table.dataset.rbHeading = heading;
+ if (!table.querySelector('caption') && !table.closest('.rb-meta-card')) {
+ const caption = document.createElement('caption');
+ caption.className = 'rb-visually-hidden';
+ caption.textContent = heading;
+ table.prepend(caption);
+ }
+ stackActionTable(table);
+ markScrollable(table);
+ filterTable(table, index, heading);
+ });
+ const recheck = () => document.querySelectorAll('.sl-markdown-content table').forEach(markScrollable);
+ let timer = 0;
+ addEventListener('resize', () => {
+ clearTimeout(timer);
+ timer = window.setTimeout(recheck, 150);
+ });
+ document.addEventListener('toggle', recheck, true);
+ document.addEventListener('click', (event) => {
+ if ((event.target as Element).closest?.('[role="tab"]')) setTimeout(recheck, 0);
+ });
+};
+
+// files/?hash= reads one shard of the lookup, named by the first two hex
+// characters of the value.
+
+const lookupShard = (value: string): Promise- =>
+ fetch(new URL(`api/v1/lookup/${value.slice(0, 2)}.json`, siteRoot()))
+ .then((response) => (response.ok ? response.json() : { items: [] }))
+ .then((shard) => shard.items || []);
+
+const renderMatches = (status: HTMLElement, value: string, items: Item[]) => {
+ status.textContent = '';
+ const heading = document.createElement('p');
+ heading.textContent = `${items.length} ${items.length === 1 ? 'match' : 'matches'} for ${HASH_LENGTHS[value.length]} ${value}:`;
+ status.append(heading);
+ for (const [layer, label] of Object.entries(LAYER_LABELS)) {
+ const found = items.filter((item) => item.layer === layer);
+ if (!found.length) continue;
+ const title = document.createElement('p');
+ const strong = document.createElement('strong');
+ strong.textContent = label;
+ title.append(strong);
+ const list = document.createElement('ul');
+ for (const item of found) {
+ const row = document.createElement('li');
+ const link = document.createElement('a');
+ link.href = new URL(item.url, siteRoot()).href;
+ const code = document.createElement('code');
+ code.textContent = item.name || item.value;
+ link.append(code);
+ row.append(link);
+ if (item.owner) row.append(` (${item.owner})`);
+ list.append(row);
+ }
+ status.append(title, list);
+ }
+};
+
+// files/?sha1=
is a file's content address and opens its card
+// wherever it is now; files/?hash= also takes an MD5, CRC32 or SHA256 and
+// lists every page that names the value.
+const resolveFileAddress = () => {
+ const status = document.getElementById('rb-file-lookup-status');
+ if (!status) return;
+ const params = new URLSearchParams(location.search);
+ const permalink = params.has('sha1');
+ const value = normalizeHash(params.get('sha1') || params.get('hash') || '');
+ if (!value) return;
+ const input = document.getElementById('rb-hash') as HTMLInputElement | null;
+ if (input) input.value = value;
+ const prefix = permalink && /^[0-9a-f]{12}$/.test(value);
+ if (!prefix && !isHash(value)) {
+ status.textContent = 'A hash is 8 (CRC32), 32 (MD5), 40 (SHA1) or 64 (SHA256) hexadecimal characters.';
+ return;
+ }
+ status.textContent = 'Looking it up...';
+ lookupShard(value)
+ .then((items) => {
+ const matches = items.filter((item) =>
+ prefix ? item.algorithm === 'sha1' && item.value.startsWith(value) : item.value === value);
+ const cards = matches.filter((item) => item.layer === 'collection' || item.layer === 'data-directory');
+ if (permalink && new Set(cards.map((item) => item.url)).size === 1) {
+ location.replace(new URL(cards[0].url, siteRoot()));
+ } else if (matches.length) {
+ renderMatches(status, prefix ? matches[0].value : value, prefix ? cards : matches);
+ } else {
+ status.textContent = 'No file the site knows has this hash.';
+ }
+ })
+ .catch(() => {
+ status.textContent = 'The file index could not be read.';
+ });
+};
+
+// A file card moved to another page keeps its address: an id of the form
+// sha1-<12 hex> that this page does not hold is looked up by content, and
+// an emulator card listed in the table of a split page opens its page.
+const followMovedCard = () => {
+ const id = decodeURIComponent(location.hash.slice(1));
+ if (!id) return;
+ const target = document.getElementById(id);
+ const link = target?.closest('.rb-card-index tr')?.querySelector('a[href]');
+ if (link) {
+ location.replace(link.href);
+ return;
+ }
+ const match = /^sha1-([0-9a-f]{12})$/.exec(id);
+ if (match && !target) location.replace(new URL(`files/?sha1=${match[1]}`, siteRoot()));
+};
+
+// The site search holds no hashes: Enter on one opens the lookup.
+const hashSearch = () => {
+ document.addEventListener(
+ 'keydown',
+ (event) => {
+ if (event.key !== 'Enter') return;
+ const input = event.target as HTMLInputElement;
+ if (!input.matches?.('site-search dialog input')) return;
+ const value = normalizeHash(input.value);
+ if (!isHash(value)) return;
+ event.preventDefault();
+ event.stopPropagation();
+ location.assign(new URL(`files/?hash=${value}`, siteRoot()));
+ },
+ true,
+ );
+};
+
+// A glossary term shows its definition on hover; a tap or Enter shows it
+// on a phone or to a keyboard, and Escape or a tap elsewhere closes it.
+const glossaryTerms = () => {
+ const terms = document.querySelectorAll('abbr.rb-term');
+ if (!terms.length) return;
+ const close = () => document.querySelectorAll('abbr.rb-term[data-open]').forEach((term) => term.removeAttribute('data-open'));
+ for (const term of terms) {
+ term.tabIndex = 0;
+ term.setAttribute('role', 'button');
+ term.setAttribute('aria-label', `${term.textContent}: ${term.title}`);
+ term.dataset.definition = term.title;
+ const toggle = (event: Event) => {
+ event.stopPropagation();
+ const open = term.hasAttribute('data-open');
+ close();
+ if (!open) term.setAttribute('data-open', '');
+ };
+ term.addEventListener('click', toggle);
+ term.addEventListener('keydown', (event) => {
+ if (event.key === 'Enter' || event.key === ' ') {
+ event.preventDefault();
+ toggle(event);
+ } else if (event.key === 'Escape') close();
+ });
+ }
+ document.addEventListener('click', close);
+};
+
+// The 404 page: a search button, and the list a moved page belongs to.
+const notFound = () => {
+ document.querySelector('[data-rb-open-search]')?.addEventListener('click', () => {
+ document.querySelector('site-search button[data-open-modal]')?.click();
+ });
+ const hint = document.getElementById('rb-notfound-hint');
+ const match = location.pathname.match(/\/(emulators|systems|platforms)\/([^/]+)\/?$/);
+ if (!hint || !match) return;
+ const link = document.createElement('a');
+ link.href = new URL(`${match[1]}/`, siteRoot()).href;
+ link.textContent = `the list of ${match[1]}`;
+ hint.append(`No page is named ${decodeURIComponent(match[2])} here. It may be listed under another name in `, link, '.');
+ hint.hidden = false;
+};
+
+resolveFileAddress();
+followMovedCard();
+enhanceTables();
+hashSearch();
+glossaryTerms();
+notFound();
diff --git a/website/src/styles/retrobios.css b/website/src/styles/retrobios.css
new file mode 100644
index 00000000..e8385889
--- /dev/null
+++ b/website/src/styles/retrobios.css
@@ -0,0 +1,557 @@
+/* RetroBIOS on top of Starlight.
+
+ Starlight's dark theme is :root and its light theme
+ :root[data-theme='light']. Colour pairs are the ones measured for WCAG AA
+ on the previous site, in both themes.
+
+ Classes the generator and the wiki pages use:
+ .rb-lead first paragraph under a page title
+ .rb-level-* level label (start, setup, explore, reference, contribute)
+ .rb-cards, .rb-card card grid
+ .rb-steps ordered list drawn as numbered steps
+ .rb-button a link drawn as a button (.rb-button-primary: filled)
+ .rb-terminal wraps a code block that shows terminal output
+ .rb-cmd wraps a command that must wrap instead of scrolling
+ code.rb-path a folder path shown as a chip
+ .rb-figure a generated diagram (scripts/sitediagrams.py)
+ .rb-bar inline coverage bar, width from --v
+ .rb-details-* collapsible block, in the callout colour of its role
+ abbr.rb-term a glossary term, its definition on hover or tap
+*/
+
+:root {
+ --sl-font: system-ui, -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif;
+ --sl-font-mono: ui-monospace, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
+ --sl-color-accent-low: #1f2147;
+ --sl-color-accent: #8c9cf0;
+ --sl-color-accent-high: #c9d2ff;
+
+ --rb-link: #aebcff;
+ --rb-accent: #ff9a8f;
+ --rb-surface: #252838;
+ --rb-border: #3a3d52;
+ --rb-border-strong: #5a5e7a;
+ --rb-text-secondary: #a6adc8;
+ --rb-ok-fg: #a5d6a7;
+ --rb-ok-bg: #1b3a1f;
+ --rb-warn-fg: #ffcc80;
+ --rb-warn-bg: #7a2e00;
+ --rb-err-fg: #ef9a9a;
+ --rb-err-bg: #7f1414;
+ --rb-info-fg: #90caf9;
+ --rb-info-bg: #0d2a55;
+ --rb-muted-fg: #cfd8dc;
+ --rb-muted-bg: #37474f;
+ --rb-level-start: #a5d6a7;
+ --rb-level-setup: #90caf9;
+ --rb-level-explore: #ce93d8;
+ --rb-level-reference: #cfd8dc;
+ --rb-level-contribute: #ffcc80;
+ --rb-terminal-bg: #12131c;
+ --rb-terminal-fg: #e6e6f0;
+ --rb-terminal-label: #9aa0c3;
+ --rb-diagram-fill: #252838;
+ --rb-diagram-stroke: #7c82a3;
+ --rb-diagram-accent: #aebcff;
+}
+
+:root[data-theme='light'] {
+ --sl-color-accent-low: #e3e5f6;
+ --sl-color-accent: #3b3f7a;
+ --sl-color-accent-high: #2b2e5c;
+
+ --rb-link: #273c8f;
+ --rb-accent: #b42318;
+ --rb-surface: #f5f6fa;
+ --rb-border: #e0e3eb;
+ --rb-border-strong: #b8bdcc;
+ --rb-text-secondary: #546e7a;
+ --rb-ok-fg: #1b5e20;
+ --rb-ok-bg: #e8f5e9;
+ --rb-warn-fg: #a33f00;
+ --rb-warn-bg: #fff3e0;
+ --rb-err-fg: #b71c1c;
+ --rb-err-bg: #ffebee;
+ --rb-info-fg: #0d47a1;
+ --rb-info-bg: #e3f2fd;
+ --rb-muted-fg: #455a64;
+ --rb-muted-bg: #eceff1;
+ --rb-level-start: #1b5e20;
+ --rb-level-setup: #0d47a1;
+ --rb-level-explore: #6a1b9a;
+ --rb-level-reference: #455a64;
+ --rb-level-contribute: #a33f00;
+ --rb-terminal-bg: #1b1d2e;
+ --rb-diagram-fill: #f5f6fa;
+ --rb-diagram-stroke: #8c92ac;
+ --rb-diagram-accent: #3b3f7a;
+}
+
+/* Links and focus */
+.sl-markdown-content a:not(.rb-button):not(.rb-badge):not(.rb-permalink) {
+ color: var(--rb-link);
+ text-decoration-thickness: 0.08em;
+ text-underline-offset: 0.14em;
+}
+.sl-markdown-content a:not(.rb-button):hover { text-decoration-thickness: 0.14em; }
+.sl-markdown-content a code { color: var(--rb-link); }
+:where(a, button, input, select, textarea, summary, abbr, [tabindex]):focus-visible {
+ outline: 3px solid var(--rb-accent);
+ outline-offset: 3px;
+}
+
+/* Type scale */
+.sl-markdown-content h2 { font-weight: 600; letter-spacing: -0.005em; }
+.sl-markdown-content h3 { font-weight: 600; }
+.sl-markdown-content .rb-lead { font-size: 1.05em; line-height: 1.55; max-width: 44em; }
+
+/* Breadcrumbs above the title */
+.rb-breadcrumbs ol {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.15rem 0;
+ margin: 0 0 0.4rem;
+ padding: 0;
+ list-style: none;
+ font-size: var(--sl-text-sm);
+ color: var(--rb-text-secondary);
+}
+.rb-breadcrumbs li + li::before { content: '/'; padding: 0 0.45rem; color: var(--rb-border-strong); }
+.rb-breadcrumbs a {
+ display: inline-block;
+ min-height: 24px;
+ line-height: 24px;
+ color: var(--rb-text-secondary);
+ text-decoration: none;
+}
+.rb-breadcrumbs a:hover { color: var(--rb-link); text-decoration: underline; }
+
+/* Level label above a title */
+.rb-level {
+ display: inline-block;
+ margin: 0 0 0.5rem;
+ padding: 0.05rem 0.45rem;
+ border: 1px solid currentColor;
+ border-radius: 2px;
+ font-size: 0.72rem;
+ font-weight: 600;
+ letter-spacing: 0.01em;
+ line-height: 1.6;
+ color: var(--rb-level-reference);
+}
+.rb-level-start { color: var(--rb-level-start); }
+.rb-level-setup { color: var(--rb-level-setup); }
+.rb-level-explore { color: var(--rb-level-explore); }
+.rb-level-reference { color: var(--rb-level-reference); }
+.rb-level-contribute { color: var(--rb-level-contribute); }
+
+/* Buttons */
+.sl-markdown-content .rb-button {
+ display: inline-flex;
+ align-items: center;
+ min-height: 44px;
+ padding: 0.5rem 1.1rem;
+ margin: 0.25rem 0.4rem 0.25rem 0;
+ border: 2px solid var(--rb-link);
+ border-radius: 4px;
+ color: var(--rb-link);
+ font-weight: 600;
+ text-decoration: none;
+}
+.sl-markdown-content .rb-button-primary { background: var(--rb-link); color: var(--sl-color-black); }
+.sl-markdown-content .rb-button:is(:hover, :focus) {
+ background: var(--rb-accent);
+ border-color: var(--rb-accent);
+ color: var(--sl-color-black);
+}
+.sl-markdown-content table .rb-button { font-size: 0.8rem; padding: 0.4rem 0.8rem; }
+
+/* Badges */
+.rb-badge {
+ display: inline-block;
+ padding: 0 0.4em;
+ border-radius: 2px;
+ font-size: 0.75rem;
+ font-weight: 600;
+ line-height: 1.6;
+ white-space: nowrap;
+}
+.rb-badge-success { background: var(--rb-ok-bg); color: var(--rb-ok-fg); }
+.rb-badge-warning { background: var(--rb-warn-bg); color: var(--rb-warn-fg); }
+.rb-badge-danger { background: var(--rb-err-bg); color: var(--rb-err-fg); }
+.rb-badge-info { background: var(--rb-info-bg); color: var(--rb-info-fg); }
+.rb-badge-muted { background: var(--rb-muted-bg); color: var(--rb-muted-fg); }
+.sl-markdown-content .rb-badge a,
+.sl-markdown-content a.rb-badge { color: inherit; }
+/* A badge link sits in a dense list: it keeps a 24 px target (WCAG 2.5.8). */
+.sl-markdown-content :is(.rb-sys-file, .rb-file-entry) li:has(.rb-badge a) { min-height: 24px; line-height: 24px; }
+.sl-markdown-content .rb-badge a { display: inline-block; min-width: 24px; line-height: 24px; text-align: center; }
+
+/* Headline figures */
+.rb-stats {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
+ gap: 0.6rem 1rem;
+ margin: 1rem 0 1.5rem;
+ padding: 0.8rem 1rem;
+ border: 1px solid var(--rb-border);
+ border-radius: 4px;
+ background: var(--rb-surface);
+}
+.sl-markdown-content .rb-stats > *,
+.sl-markdown-content .rb-stat > * { margin: 0; }
+.rb-stat-value { display: block; font-size: 1.25rem; font-weight: 700; font-variant-numeric: tabular-nums; }
+.rb-stat-label { display: block; font-size: 0.75rem; color: var(--rb-text-secondary); }
+
+/* Logos float right beside the opening paragraph */
+.sl-markdown-content img[align='right'] {
+ float: right;
+ width: 56px;
+ height: 56px;
+ object-fit: contain;
+ margin: 0 0 0.6rem 1rem;
+ padding: 4px;
+ border: 1px solid var(--rb-border);
+ border-radius: 4px;
+ background: #fff;
+}
+.sl-markdown-content img[src*='assets/logos/'] { padding: 2px; border-radius: 2px; background: #fff; }
+.sl-markdown-content :is(h2, h3) img { display: inline; vertical-align: middle; margin: 0 0.4rem 0 0; }
+
+/* Card grids */
+.rb-cards {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
+ gap: 0.8rem;
+ margin: 1rem 0 1.5rem;
+}
+.sl-markdown-content .rb-cards > .rb-card {
+ display: flex;
+ flex-direction: column;
+ gap: 0.35rem;
+ margin: 0;
+ padding: 0.8rem;
+ border: 1px solid var(--rb-border);
+ border-radius: 4px;
+ background: var(--sl-color-bg);
+}
+.rb-card:is(:hover, :focus-within) { border-color: var(--rb-border-strong); }
+.sl-markdown-content .rb-card > * { margin: 0; }
+.sl-markdown-content .rb-card .rb-button { margin-top: auto; align-self: flex-start; }
+.sl-markdown-content .rb-card-logo {
+ width: 40px;
+ height: 40px;
+ object-fit: contain;
+ padding: 4px;
+ border: 1px solid var(--rb-border);
+ border-radius: 4px;
+ background: #fff;
+ vertical-align: middle;
+}
+
+/* Numbered steps */
+.sl-markdown-content .rb-steps > ol {
+ counter-reset: rb-step;
+ list-style: none;
+ padding-left: 0;
+}
+.sl-markdown-content .rb-steps > ol > li {
+ counter-increment: rb-step;
+ position: relative;
+ margin: 0 0 1.2rem;
+ padding-left: 2.6rem;
+ min-height: 1.8rem;
+}
+.sl-markdown-content .rb-steps > ol > li::before {
+ content: counter(rb-step);
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 1.8rem;
+ height: 1.8rem;
+ border-radius: 4px;
+ background: var(--rb-link);
+ color: var(--sl-color-black);
+ font-weight: 700;
+ line-height: 1.8rem;
+ text-align: center;
+}
+
+/* Path chip */
+.sl-markdown-content code.rb-path {
+ padding: 0.15rem 0.45rem;
+ border: 1px solid var(--rb-border);
+ border-radius: 2px;
+ font-size: 0.8em;
+ overflow-wrap: anywhere;
+}
+
+/* Inline coverage bar beside a number */
+.rb-bar {
+ display: inline-block;
+ width: 5rem;
+ height: 0.4rem;
+ margin-right: 0.4rem;
+ border-radius: 2px;
+ background: var(--rb-border);
+ vertical-align: middle;
+}
+.rb-bar > span { display: block; width: var(--v, 0%); height: 100%; border-radius: 2px; background: var(--rb-ok-fg); }
+
+/* Tables */
+.sl-markdown-content table { font-variant-numeric: tabular-nums; }
+.sl-markdown-content table th { font-weight: 600; }
+.sl-markdown-content table[role='region']:focus-visible { outline: 3px solid var(--rb-accent); outline-offset: 2px; }
+
+/* Key and value cards: the empty header row is not drawn */
+.rb-meta-card thead,
+.sl-markdown-content table thead:not(:has(th:not(:empty))) { display: none; }
+.rb-meta-card td { overflow-wrap: anywhere; }
+.rb-meta-card td:first-child { font-weight: 600; color: var(--rb-text-secondary); white-space: nowrap; width: 140px; }
+
+.rb-timestamp {
+ font-size: 0.8rem;
+ color: var(--rb-text-secondary);
+ margin-top: 2rem;
+ padding-top: 1rem;
+ border-top: 1px solid var(--rb-border);
+}
+
+/* File cards on system and emulator pages */
+.sl-markdown-content :is(.rb-sys-file, .rb-file-entry) {
+ border-top: 1px solid var(--rb-border);
+ padding: 0.75rem 0 0.25rem;
+}
+.sl-markdown-content :is(.rb-sys-file, .rb-file-entry) > :first-child { margin-top: 0; }
+.sl-markdown-content .rb-file-title {
+ margin: 0 0 0.3rem;
+ font-size: 1rem;
+ font-weight: 400;
+ letter-spacing: normal;
+ scroll-margin-top: 5rem;
+}
+.sl-markdown-content .rb-file-title code { font-weight: 700; }
+.sl-markdown-content .rb-search-skip > ul { margin-top: 0.1rem; }
+.sl-markdown-content :is(.rb-sys-file, .rb-file-entry) ul {
+ margin: 0.3rem 0 0;
+ padding-left: 1.2rem;
+ font-size: 0.85rem;
+}
+.sl-markdown-content :is(.rb-sys-file, .rb-file-entry) li { margin: 0.1rem 0; }
+.rb-hash {
+ font-family: var(--sl-font-mono);
+ font-size: 0.78rem;
+ color: var(--rb-text-secondary);
+ overflow-wrap: anywhere;
+}
+.rb-hash code { background: none; padding: 0; overflow-wrap: anywhere; }
+.rb-permalink {
+ margin-left: 0.4rem;
+ font-size: 0.75rem;
+ color: var(--rb-text-secondary);
+ text-decoration: none;
+ opacity: 0;
+}
+:is(.rb-sys-file, .rb-file-entry):hover .rb-permalink,
+.rb-permalink:focus { opacity: 1; }
+:is(.rb-sys-file, .rb-file-entry):has(> .rb-file-title:target) { background: var(--rb-surface); }
+
+/* Collapsible blocks keep the callout colour of their role */
+.sl-markdown-content details.rb-details {
+ border-inline-start: 4px solid var(--sl-color-blue);
+ background: var(--sl-color-blue-low);
+ padding: 0.6rem 1rem;
+}
+.sl-markdown-content details.rb-details-tip { border-color: var(--sl-color-purple); background: var(--sl-color-purple-low); }
+.sl-markdown-content details.rb-details-caution { border-color: var(--sl-color-orange); background: var(--sl-color-orange-low); }
+.sl-markdown-content details.rb-details-danger { border-color: var(--sl-color-red); background: var(--sl-color-red-low); }
+.sl-markdown-content details.rb-details > summary {
+ min-height: 24px;
+ font-weight: 600;
+ cursor: pointer;
+}
+.sl-markdown-content details.rb-details[open] > summary { margin-bottom: 0.6rem; }
+
+/* Terminal output: what the screen shows, not something to type */
+.sl-markdown-content .rb-terminal .expressive-code pre { background: var(--rb-terminal-bg); color: var(--rb-terminal-fg); }
+
+/* Diagrams written by scripts/sitediagrams.py follow the theme */
+.sl-markdown-content .rb-figure { margin: 1.2rem 0 1.6rem; }
+.sl-markdown-content .rb-figure svg { display: block; width: 100%; height: auto; max-width: 52rem; color: var(--sl-color-text); }
+.sl-markdown-content .rb-figure :is(figcaption, .rb-figure-caption) {
+ display: block;
+ max-width: 44em;
+ margin-top: 0.4rem;
+ font-size: 0.8rem;
+ color: var(--rb-text-secondary);
+}
+.rb-diagram text { font-family: var(--sl-font); fill: currentColor; }
+.rb-diagram .rb-d-box { fill: var(--rb-diagram-fill); stroke: var(--rb-diagram-stroke); }
+.rb-diagram .rb-d-accent { stroke: var(--rb-diagram-accent); }
+.rb-diagram .rb-d-line { stroke: var(--rb-diagram-stroke); }
+.rb-diagram .rb-d-head { fill: var(--rb-diagram-stroke); }
+.rb-diagram .rb-d-num { fill: var(--rb-diagram-accent); }
+.rb-diagram .rb-d-numtext { fill: var(--sl-color-bg); }
+.rb-diagram .rb-d-muted { fill: var(--rb-text-secondary); }
+.rb-diagram-narrow { display: none !important; }
+@media (max-width: 37.5em) {
+ .rb-diagram-wide { display: none !important; }
+ .rb-diagram-narrow { display: block !important; }
+}
+
+/* Glossary terms */
+abbr.rb-term {
+ position: relative;
+ text-decoration: underline dotted;
+ text-underline-offset: 0.2em;
+ cursor: help;
+}
+abbr.rb-term[data-open]::after {
+ content: attr(data-definition);
+ position: absolute;
+ z-index: 10;
+ top: calc(100% + 0.3rem);
+ left: 0;
+ width: max-content;
+ max-width: min(20rem, 80vw);
+ padding: 0.4rem 0.6rem;
+ border: 1px solid var(--rb-border-strong);
+ border-radius: 4px;
+ background: var(--sl-color-bg);
+ color: var(--sl-color-text);
+ font-size: 0.85rem;
+ font-weight: 400;
+ line-height: 1.4;
+ white-space: normal;
+}
+
+/* Forms: lookup, picker, table filters */
+.rb-file-lookup,
+.rb-picker fieldset {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ align-items: center;
+}
+.sl-markdown-content :is(.rb-file-lookup, .rb-picker) :is(input, select, button) {
+ min-height: 44px;
+ padding: 0 0.6rem;
+ border: 1px solid var(--rb-border-strong);
+ border-radius: 4px;
+ background: var(--sl-color-bg);
+ color: var(--sl-color-text);
+ font: inherit;
+}
+.sl-markdown-content .rb-file-lookup input { flex: 1 1 20rem; font-family: var(--sl-font-mono); }
+.sl-markdown-content :is(.rb-file-lookup, .rb-picker) button {
+ background: var(--rb-link);
+ color: var(--sl-color-black);
+ font-weight: 600;
+ border: 0;
+}
+.rb-visually-hidden {
+ position: absolute !important;
+ width: 1px !important;
+ height: 1px !important;
+ padding: 0 !important;
+ margin: -1px !important;
+ overflow: hidden !important;
+ clip: rect(0, 0, 0, 0) !important;
+ white-space: nowrap !important;
+ border: 0 !important;
+}
+.rb-table-filter {
+ display: grid;
+ grid-template-columns: minmax(14rem, 28rem) auto;
+ gap: 0.35rem 0.8rem;
+ align-items: end;
+ margin: 1rem 0 0.45rem;
+}
+.sl-markdown-content .rb-table-filter > * { margin: 0; }
+.rb-table-filter label { grid-column: 1 / -1; color: var(--rb-text-secondary); font-size: 0.78rem; font-weight: 700; }
+.rb-table-filter input {
+ width: 100%;
+ min-height: 44px;
+ border: 1px solid var(--rb-text-secondary);
+ border-radius: 2px;
+ background: var(--sl-color-bg);
+ color: var(--sl-color-text);
+ padding: 0.55rem 0.75rem;
+ font: inherit;
+}
+.rb-table-filter-count { align-self: center; color: var(--rb-text-secondary); font-size: 0.8rem; white-space: nowrap; }
+
+/* 404 */
+.sl-markdown-content .rb-notfound-search {
+ display: inline-flex;
+ align-items: center;
+ min-height: 44px;
+ padding: 0.55em 1.2em;
+ border: 2px solid var(--rb-link);
+ border-radius: 4px;
+ background: none;
+ color: var(--rb-link);
+ font: inherit;
+ font-weight: 700;
+ cursor: pointer;
+}
+
+/* Licence line under every page */
+.rb-copyright { margin: 1.5rem 0 0; font-size: var(--sl-text-xs); color: var(--sl-color-gray-3); }
+
+@media (max-width: 50rem) {
+ /* Tables with an action column become one block per row, so the button
+ is never behind a horizontal scroll (site.ts marks them). */
+ .sl-markdown-content table[data-rb-stack] { display: block; width: 100%; overflow: visible; }
+ .sl-markdown-content table[data-rb-stack] thead {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ }
+ .sl-markdown-content table[data-rb-stack] :is(tbody, tr) { display: block; }
+ .sl-markdown-content table[data-rb-stack] tr { padding: 0.5rem 0; border-top: 1px solid var(--rb-border); }
+ .sl-markdown-content table[data-rb-stack] td {
+ display: block;
+ position: relative;
+ min-height: 1.6rem;
+ padding: 0.25rem 0.6rem 0.25rem 8.4rem;
+ border: 0;
+ text-align: left !important;
+ overflow-wrap: anywhere;
+ }
+ .sl-markdown-content table[data-rb-stack] td::before {
+ content: attr(data-label);
+ position: absolute;
+ top: 0.25rem;
+ left: 0.6rem;
+ width: 7.4rem;
+ font-weight: 600;
+ color: var(--rb-text-secondary);
+ }
+ .sl-markdown-content table[data-rb-stack] td:empty { display: none; }
+
+ /* On a phone every command wraps: the end of a one-liner is never off-screen. */
+ .sl-markdown-content .expressive-code pre > code { white-space: pre-wrap; overflow-wrap: anywhere; }
+ .sl-markdown-content :is(.rb-sys-file, .rb-file-entry) ul,
+ .rb-hash { font-size: 0.75rem; }
+}
+
+@media (max-width: 600px) {
+ .rb-table-filter { grid-template-columns: 1fr; }
+ .rb-table-filter label { grid-column: auto; }
+ .rb-stats { grid-template-columns: repeat(auto-fit, minmax(5.5rem, 1fr)); }
+ .rb-stat-value { font-size: 1.1rem; }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ *,
+ *::before,
+ *::after {
+ scroll-behavior: auto !important;
+ transition-duration: 0.01ms !important;
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ }
+}
diff --git a/website/tools/axe.mjs b/website/tools/axe.mjs
new file mode 100644
index 00000000..2b01fdec
--- /dev/null
+++ b/website/tools/axe.mjs
@@ -0,0 +1,24 @@
+// axe-core (WCAG 2.2 A/AA rules) in light and dark, one line per page and scheme.
+// node axe.mjs ...
+import { chromium } from 'playwright-core';
+import { readFile } from 'node:fs/promises';
+import { createRequire } from 'node:module';
+
+const require = createRequire(import.meta.url);
+const axeSource = await readFile(require.resolve('axe-core/axe.min.js'), 'utf8');
+const [base, ...pages] = process.argv.slice(2);
+const browser = await chromium.launch({ executablePath: '/usr/bin/google-chrome' });
+for (const scheme of ['light', 'dark']) {
+ const context = await browser.newContext({ viewport: { width: 390, height: 844 }, colorScheme: scheme });
+ const page = await context.newPage();
+ for (const path of pages) {
+ await page.goto(new URL(path, base).href, { waitUntil: 'networkidle' });
+ await page.addScriptTag({ content: axeSource });
+ const result = await page.evaluate(() =>
+ window.axe.run(document, { runOnly: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'] }));
+ const violations = result.violations.map((v) => `${v.id}(${v.nodes.length})`);
+ console.log(JSON.stringify({ page: path || '/', scheme, violations }));
+ }
+ await context.close();
+}
+await browser.close();
diff --git a/website/tools/check_tools.mjs b/website/tools/check_tools.mjs
new file mode 100644
index 00000000..91c2e7cd
--- /dev/null
+++ b/website/tools/check_tools.mjs
@@ -0,0 +1,44 @@
+// End-to-end check of the two interactive pages, in a real browser.
+// node check_tools.mjs
+import { chromium } from 'playwright-core';
+import assert from 'node:assert/strict';
+
+const base = process.argv[2] ?? 'http://127.0.0.1:4321/retrobios/';
+const browser = await chromium.launch({ executablePath: '/usr/bin/google-chrome' });
+const page = await browser.newPage();
+const errors = [];
+page.on('pageerror', (error) => errors.push(error.message));
+
+// Hash lookup: an MD5 lists every layer, a SHA1 permalink opens the card.
+await page.goto(`${base}files/?hash=490f666e1afb15b7362b406ed1cea246`, { waitUntil: 'networkidle' });
+const lookup = await page.textContent('#rb-file-lookup-status');
+assert.match(lookup, /match/);
+console.log('lookup md5:', lookup.slice(0, 160).replace(/\s+/g, ' '));
+await page.goto(`${base}files/?sha1=0555c6fae8906f3f09baf5988f00e55f88e9f30b`, { waitUntil: 'networkidle' });
+assert.ok(page.url().includes('systems/sony/#sha1-0555c6fae890'), page.url());
+console.log('permalink sha1 ->', page.url());
+
+// Picker on Get your files: Batocera, PlayStation only.
+const transfers = [];
+page.on('response', async (response) => {
+ if (response.url().includes('/data/picker/')) transfers.push([response.url(), (await response.body()).length]);
+});
+await page.goto(`${base}which-pack/?platform=batocera&system=sony-playstation`, { waitUntil: 'networkidle' });
+await page.waitForSelector('.rb-pick-output:not([hidden])');
+const result = await page.textContent('.rb-pick-result');
+const command = await page.textContent('.rb-pick-cmd code');
+const rows = await page.locator('.rb-pick-output tbody tr').count();
+console.log('picker:', result);
+console.log('command:', command);
+console.log('rows:', rows, 'fetched:', transfers.map(([url, bytes]) => `${url.split('/').pop()} ${bytes} B`).join(', '));
+assert.match(command, /--platform batocera --system sony-playstation$/);
+assert.ok(rows > 0);
+
+// Unchecking everything empties the result; "All" restores the full command.
+await page.click('[data-none]');
+assert.match(await page.textContent('.rb-pick-result'), /Choose at least one system/);
+await page.click('[data-all]');
+assert.match(await page.textContent('.rb-pick-cmd code'), /--platform batocera$/);
+assert.deepEqual(errors, []);
+console.log('ok');
+await browser.close();
diff --git a/website/tools/lighthouse.py b/website/tools/lighthouse.py
new file mode 100644
index 00000000..224f9abd
--- /dev/null
+++ b/website/tools/lighthouse.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+"""Lighthouse mobile (default form factor) on a list of pages, one summary line each.
+
+ python lighthouse.py = ... -- ...
+
+Needs `npx lighthouse` and Chrome at /usr/bin/google-chrome.
+"""
+from __future__ import annotations
+
+import json
+import os
+import subprocess
+import sys
+from pathlib import Path
+
+CATEGORIES = ("performance", "accessibility", "best-practices", "seo")
+
+
+def main() -> int:
+ args = sys.argv[1:]
+ out = Path(args.pop(0))
+ split = args.index("--")
+ engines = dict(item.split("=", 1) for item in args[:split])
+ pages = args[split + 1:]
+ out.mkdir(parents=True, exist_ok=True)
+ env = {**os.environ, "CHROME_PATH": "/usr/bin/google-chrome"}
+ for name, base in engines.items():
+ for page in pages:
+ slug = page.strip("/").replace("/", "_") or "home"
+ report = out / f"{name}-{slug}.json"
+ # Under WSL chrome-launcher names its profile folder with a Windows path,
+ # created relative to the working directory: run it inside the output folder.
+ subprocess.run(
+ ["npx", "--prefix", str(Path(__file__).resolve().parent.parent), "lighthouse", base + page,
+ "--quiet", "--output=json", f"--output-path={report.resolve()}",
+ "--chrome-flags=--headless=new --no-sandbox",
+ f"--only-categories={','.join(CATEGORIES)}"],
+ env=env, cwd=out, check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+ data = json.loads(report.read_text())
+ cats, audits = data["categories"], data["audits"]
+ failed = [k for k, a in audits.items()
+ if a.get("score") is not None and a["score"] < 1 and a.get("scoreDisplayMode") == "binary"]
+ print(json.dumps({
+ "engine": name, "page": page or "/",
+ **{c: round(cats[c]["score"] * 100) for c in CATEGORIES},
+ "lcp": audits["largest-contentful-paint"]["displayValue"],
+ "tbt": audits["total-blocking-time"]["displayValue"],
+ "cls": audits["cumulative-layout-shift"]["displayValue"],
+ "bytes": audits["total-byte-weight"]["displayValue"],
+ "dom": audits["dom-size"]["displayValue"],
+ "failed": failed,
+ }))
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/website/tools/measure.mjs b/website/tools/measure.mjs
new file mode 100644
index 00000000..2a854573
--- /dev/null
+++ b/website/tools/measure.mjs
@@ -0,0 +1,106 @@
+// Page weight and search measurements in a real browser.
+// node measure.mjs weight ...
+// node measure.mjs search starlight|mkdocs ...
+// Transfer sizes are bytes on the wire (gzip from serve.mjs), by type.
+import { chromium } from 'playwright-core';
+
+const [mode, base, ...rest] = process.argv.slice(2);
+const browser = await chromium.launch({ executablePath: '/usr/bin/google-chrome' });
+
+const track = (page) => {
+ const rows = [];
+ page.on('response', async (response) => {
+ const request = response.request();
+ const headers = await response.allHeaders();
+ let bytes = Number(headers['content-length'] ?? 0);
+ if (!bytes) {
+ try { bytes = (await response.body()).length; } catch { bytes = 0; }
+ }
+ rows.push({ url: response.url(), type: request.resourceType(), bytes });
+ });
+ return rows;
+};
+
+const sum = (rows, test) => rows.filter(test).reduce((total, row) => total + row.bytes, 0);
+
+if (mode === 'weight') {
+ for (const path of rest) {
+ const context = await browser.newContext({ viewport: { width: 390, height: 844 } });
+ const page = await context.newPage();
+ const rows = track(page);
+ await page.goto(new URL(path, base).href, { waitUntil: 'networkidle' });
+ await page.waitForTimeout(500);
+ const dom = await page.evaluate(() => document.getElementsByTagName('*').length);
+ const kb = (n) => (n / 1024).toFixed(1);
+ console.log(JSON.stringify({
+ page: path,
+ html_kb: kb(sum(rows, (r) => r.type === 'document')),
+ js_kb: kb(sum(rows, (r) => r.type === 'script')),
+ css_kb: kb(sum(rows, (r) => r.type === 'stylesheet')),
+ other_kb: kb(sum(rows, (r) => !['document', 'script', 'stylesheet'].includes(r.type))),
+ total_kb: kb(sum(rows, () => true)),
+ requests: rows.length,
+ dom_nodes: dom,
+ }));
+ await context.close();
+ }
+} else if (mode === 'search') {
+ const [engine, ...queries] = rest;
+ const context = await browser.newContext({ viewport: { width: 1440, height: 900 } });
+ const page = await context.newPage();
+ const rows = track(page);
+ await page.goto(base, { waitUntil: 'networkidle' });
+ await page.waitForTimeout(500);
+ const before = rows.length;
+ const first = [];
+ for (const [n, query] of queries.entries()) {
+ const mark = rows.length;
+ let results;
+ if (engine === 'starlight-ui') {
+ // The reader's path: open the dialog, type, wait for the result list.
+ if (n === 0) await page.click('button[data-open-modal]');
+ const input = page.locator('dialog input[type="text"], dialog input[type="search"]').first();
+ await input.fill(query);
+ await page.waitForSelector('.pagefind-ui__result', { timeout: 15000 });
+ await page.waitForTimeout(1200);
+ results = await page.evaluate(() => {
+ const items = [...document.querySelectorAll('.pagefind-ui__result')];
+ return {
+ count: document.querySelector('.pagefind-ui__message')?.textContent ?? '',
+ top: items.slice(0, 5).map((r) => `${r.querySelector('.pagefind-ui__result-link')?.textContent?.trim()} | ${r.querySelector('.pagefind-ui__result-link')?.getAttribute('href')}`),
+ };
+ });
+ } else if (engine === 'starlight') {
+ results = await page.evaluate(async ({ query, root, ranking }) => {
+ if (!window.__pf) {
+ window.__pf = await import(`${root}pagefind/pagefind.js`);
+ if (ranking) await window.__pf.options({ ranking });
+ }
+ const search = await window.__pf.search(query);
+ const top = await Promise.all(search.results.slice(0, 5).map((r) => r.data()));
+ return { count: search.results.length, top: top.map((d) => `${d.meta.title} | ${d.url}`) };
+ }, { query, root: new URL('/retrobios/', base).href, ranking: process.env.RB_RANKING ? JSON.parse(process.env.RB_RANKING) : null });
+ } else {
+ // Material: open the search through its share link (?q=) and read the result list.
+ await page.goto(`${base}?q=${encodeURIComponent(query)}`, { waitUntil: 'networkidle' });
+ await page.waitForFunction(
+ (q) => document.querySelector('input[data-md-component="search-query"]')?.value === q
+ && /matching|No matching/.test(document.querySelector('.md-search-result__meta')?.textContent ?? ''),
+ query, { timeout: 30000 },
+ );
+ await page.waitForTimeout(800);
+ results = await page.evaluate(() => {
+ const items = [...document.querySelectorAll('.md-search-result__item > a')];
+ const meta = document.querySelector('.md-search-result__meta')?.textContent ?? '';
+ return { count: meta, top: items.slice(0, 5).map((a) => `${a.querySelector('h1,h2')?.textContent?.trim()} | ${a.getAttribute('href')}`) };
+ });
+ }
+ await page.waitForTimeout(300);
+ const transfer = sum(rows.slice(mark), () => true);
+ if (n === 0) first.push(...rows.slice(before));
+ console.log(JSON.stringify({ query, transfer_kb: (transfer / 1024).toFixed(1), ...results }));
+ }
+ console.log(JSON.stringify({ first_search_kb: (sum(first, () => true) / 1024).toFixed(1), first_search_requests: first.length }));
+ await context.close();
+}
+await browser.close();
diff --git a/website/tools/serve.mjs b/website/tools/serve.mjs
new file mode 100644
index 00000000..1b12d881
--- /dev/null
+++ b/website/tools/serve.mjs
@@ -0,0 +1,51 @@
+// Static server shaped like GitHub Pages for measurements: gzip, no brotli,
+// max-age=600, the site under /retrobios/, real 404 page.
+// node serve.mjs
+import { createServer } from 'node:http';
+import { readFile, stat } from 'node:fs/promises';
+import { extname, join, normalize } from 'node:path';
+import { gzipSync } from 'node:zlib';
+
+const [root = 'dist', port = '4321'] = process.argv.slice(2);
+const BASE = '/retrobios/';
+const TYPES = {
+ '.html': 'text/html; charset=utf-8', '.css': 'text/css', '.js': 'text/javascript',
+ '.json': 'application/json', '.svg': 'image/svg+xml', '.png': 'image/png',
+ '.xml': 'application/xml', '.txt': 'text/plain', '.wasm': 'application/wasm',
+};
+const COMPRESS = new Set(['.html', '.css', '.js', '.json', '.svg', '.xml', '.txt']);
+
+const resolveFile = async (path) => {
+ let file = join(root, normalize(path).replace(/^(\.\.[/\\])+/, ''));
+ try {
+ if ((await stat(file)).isDirectory()) file = join(file, 'index.html');
+ await stat(file);
+ return file;
+ } catch {
+ return null;
+ }
+};
+
+createServer(async (req, res) => {
+ const url = new URL(req.url, 'http://x');
+ if (!url.pathname.startsWith(BASE)) {
+ res.writeHead(302, { location: BASE }).end();
+ return;
+ }
+ const rel = decodeURIComponent(url.pathname.slice(BASE.length));
+ let file = await resolveFile(rel);
+ let status = 200;
+ if (!file) {
+ status = 404;
+ file = await resolveFile('404.html');
+ }
+ const ext = extname(file);
+ let body = await readFile(file);
+ const headers = { 'content-type': TYPES[ext] ?? 'application/octet-stream', 'cache-control': 'max-age=600' };
+ if (COMPRESS.has(ext) && /\bgzip\b/.test(req.headers['accept-encoding'] ?? '')) {
+ body = gzipSync(body);
+ headers['content-encoding'] = 'gzip';
+ }
+ headers['content-length'] = body.length;
+ res.writeHead(status, headers).end(body);
+}).listen(Number(port), '127.0.0.1');
diff --git a/website/tools/shots.mjs b/website/tools/shots.mjs
new file mode 100644
index 00000000..61edc77a
--- /dev/null
+++ b/website/tools/shots.mjs
@@ -0,0 +1,25 @@
+// Screenshots at 390 and 1440 px, light and dark.
+// node shots.mjs ...
+import { chromium } from 'playwright-core';
+import { mkdir } from 'node:fs/promises';
+
+const [base, out, ...pages] = process.argv.slice(2);
+await mkdir(out, { recursive: true });
+const browser = await chromium.launch({ executablePath: '/usr/bin/google-chrome' });
+for (const scheme of ['light', 'dark']) {
+ for (const width of [390, 1440]) {
+ const context = await browser.newContext({
+ viewport: { width, height: width === 390 ? 844 : 900 },
+ deviceScaleFactor: 1,
+ colorScheme: scheme,
+ });
+ const page = await context.newPage();
+ for (const path of pages) {
+ await page.goto(new URL(path, base).href, { waitUntil: 'networkidle' });
+ const name = path.replace(/^\/|\/$/g, '').replace(/[/?=&]+/g, '_') || 'home';
+ await page.screenshot({ path: `${out}/${name}-${width}-${scheme}.png`, fullPage: false });
+ }
+ await context.close();
+ }
+}
+await browser.close();