diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 0000000..b3d874d
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,53 @@
+name: Build and deploy
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+concurrency:
+ group: pages-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ steps:
+ - uses: actions/checkout@v7
+
+ - uses: pnpm/action-setup@v6
+ with:
+ version: "11.3.0"
+
+ - uses: actions/setup-node@v7
+ with:
+ node-version: "24"
+ cache: pnpm
+
+ - run: pnpm install --frozen-lockfile
+
+ - run: pnpm run build
+
+ - uses: actions/upload-pages-artifact@v5
+ if: github.ref == 'refs/heads/main'
+ with:
+ path: dist
+
+ deploy:
+ if: github.ref == 'refs/heads/main'
+ needs: build
+ runs-on: ubuntu-latest
+ permissions:
+ pages: write
+ id-token: write
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - id: deployment
+ uses: actions/deploy-pages@v5
diff --git a/.gitignore b/.gitignore
index fd981fa..31147ce 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,33 @@
-_site/
-.sass-cache/
-.jekyll-cache/
-.jekyll-metadata
-# Ignore folders generated by Bundler
-.bundle/
-vendor/
+# build output
+dist/
+
+# generated types
+.astro/
+
+# dependencies
+node_modules/
+
+# logs
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# environment variables
+.env
+.env.production
+
+# macOS-specific files
+.DS_Store
+
+# jetbrains setting folder
.idea/
+
+# pagefind
+public/pagefind
+
+# pnpm
+.pnpm-store
+
+# docker compose
+compose.override.yaml
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..add50ff
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,17 @@
+# Ignore everything
+/*
+
+# Except these files & folders
+!/src
+!/public
+!/.github
+!tsconfig.json
+!astro.config.ts
+!package.json
+!.prettierrc
+!eslint.config.js
+!README.md
+!.vscode
+
+# Ignore MDX files
+*.mdx
\ No newline at end of file
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..5d921d9
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,20 @@
+{
+ "arrowParens": "avoid",
+ "semi": true,
+ "tabWidth": 2,
+ "printWidth": 80,
+ "singleQuote": false,
+ "jsxSingleQuote": false,
+ "trailingComma": "es5",
+ "bracketSpacing": true,
+ "endOfLine": "lf",
+ "plugins": ["prettier-plugin-astro", "prettier-plugin-tailwindcss"],
+ "overrides": [
+ {
+ "files": "*.astro",
+ "options": {
+ "parser": "astro"
+ }
+ }
+ ]
+}
diff --git a/404.html b/404.html
deleted file mode 100644
index 2f4e433..0000000
--- a/404.html
+++ /dev/null
@@ -1,11 +0,0 @@
----
-layout: default
-title: Page not found
-permalink: /404.html
-sitemap: false
----
-
- 404
- Page not found
- That page doesn't exist. Try the home page or the blog .
-
diff --git a/Gemfile b/Gemfile
deleted file mode 100644
index 20a25c1..0000000
--- a/Gemfile
+++ /dev/null
@@ -1,40 +0,0 @@
-source "https://rubygems.org"
-
-# Hello! This is where you manage which Jekyll version is used to run.
-# When you want to use a different version, change it below, save the
-# file and run `bundle install`. Run Jekyll with `bundle exec`, like so:
-#
-# bundle exec jekyll serve
-#
-# This will help ensure the proper Jekyll version is running.
-# Happy Jekylling!
-gem "jekyll", "~> 3.9.0"
-
-# This is the default theme for new Jekyll sites. You may change this to anything you like.
-gem "minima", "~> 2.0"
-
-# If you want to use GitHub Pages, remove the "gem "jekyll"" above and
-# uncomment the line below. To upgrade, run `bundle update github-pages`.
-gem "github-pages", "~> 228", group: :jekyll_plugins
-
-# If you have any plugins, put them here!
-group :jekyll_plugins do
- gem "jekyll-feed", "~> 0.6"
-end
-
-# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
-# and associated library.
-platforms :mingw, :x64_mingw, :mswin, :jruby do
- gem "tzinfo", "~> 1.2"
- gem "tzinfo-data"
-end
-
-# Performance-booster for watching directories on Windows
-gem "wdm", "~> 0.1.0", :platforms => [:mingw, :x64_mingw, :mswin]
-
-# kramdown v2 ships without the gfm parser by default. If you're using
-# kramdown v1, comment out this line.
-gem "kramdown-parser-gfm"
-
-
-gem "webrick", "~> 1.8"
diff --git a/Gemfile.lock b/Gemfile.lock
deleted file mode 100644
index ef5010a..0000000
--- a/Gemfile.lock
+++ /dev/null
@@ -1,276 +0,0 @@
-GEM
- remote: https://rubygems.org/
- specs:
- activesupport (7.1.2)
- base64
- bigdecimal
- concurrent-ruby (~> 1.0, >= 1.0.2)
- connection_pool (>= 2.2.5)
- drb
- i18n (>= 1.6, < 2)
- minitest (>= 5.1)
- mutex_m
- tzinfo (~> 2.0)
- addressable (2.8.6)
- public_suffix (>= 2.0.2, < 6.0)
- base64 (0.2.0)
- bigdecimal (3.1.5)
- coffee-script (2.4.1)
- coffee-script-source
- execjs
- coffee-script-source (1.11.1)
- colorator (1.1.0)
- commonmarker (0.23.10)
- concurrent-ruby (1.2.2)
- connection_pool (2.4.1)
- dnsruby (1.70.0)
- simpleidn (~> 0.2.1)
- drb (2.2.0)
- ruby2_keywords
- em-websocket (0.5.3)
- eventmachine (>= 0.12.9)
- http_parser.rb (~> 0)
- ethon (0.16.0)
- ffi (>= 1.15.0)
- eventmachine (1.2.7)
- execjs (2.9.1)
- faraday (2.7.12)
- base64
- faraday-net_http (>= 2.0, < 3.1)
- ruby2_keywords (>= 0.0.4)
- faraday-net_http (3.0.2)
- ffi (1.16.3)
- forwardable-extended (2.6.0)
- gemoji (3.0.1)
- github-pages (228)
- github-pages-health-check (= 1.17.9)
- jekyll (= 3.9.3)
- jekyll-avatar (= 0.7.0)
- jekyll-coffeescript (= 1.1.1)
- jekyll-commonmark-ghpages (= 0.4.0)
- jekyll-default-layout (= 0.1.4)
- jekyll-feed (= 0.15.1)
- jekyll-gist (= 1.5.0)
- jekyll-github-metadata (= 2.13.0)
- jekyll-include-cache (= 0.2.1)
- jekyll-mentions (= 1.6.0)
- jekyll-optional-front-matter (= 0.3.2)
- jekyll-paginate (= 1.1.0)
- jekyll-readme-index (= 0.3.0)
- jekyll-redirect-from (= 0.16.0)
- jekyll-relative-links (= 0.6.1)
- jekyll-remote-theme (= 0.4.3)
- jekyll-sass-converter (= 1.5.2)
- jekyll-seo-tag (= 2.8.0)
- jekyll-sitemap (= 1.4.0)
- jekyll-swiss (= 1.0.0)
- jekyll-theme-architect (= 0.2.0)
- jekyll-theme-cayman (= 0.2.0)
- jekyll-theme-dinky (= 0.2.0)
- jekyll-theme-hacker (= 0.2.0)
- jekyll-theme-leap-day (= 0.2.0)
- jekyll-theme-merlot (= 0.2.0)
- jekyll-theme-midnight (= 0.2.0)
- jekyll-theme-minimal (= 0.2.0)
- jekyll-theme-modernist (= 0.2.0)
- jekyll-theme-primer (= 0.6.0)
- jekyll-theme-slate (= 0.2.0)
- jekyll-theme-tactile (= 0.2.0)
- jekyll-theme-time-machine (= 0.2.0)
- jekyll-titles-from-headings (= 0.5.3)
- jemoji (= 0.12.0)
- kramdown (= 2.3.2)
- kramdown-parser-gfm (= 1.1.0)
- liquid (= 4.0.4)
- mercenary (~> 0.3)
- minima (= 2.5.1)
- nokogiri (>= 1.13.6, < 2.0)
- rouge (= 3.26.0)
- terminal-table (~> 1.4)
- github-pages-health-check (1.17.9)
- addressable (~> 2.3)
- dnsruby (~> 1.60)
- octokit (~> 4.0)
- public_suffix (>= 3.0, < 5.0)
- typhoeus (~> 1.3)
- html-pipeline (2.14.3)
- activesupport (>= 2)
- nokogiri (>= 1.4)
- http_parser.rb (0.8.0)
- i18n (1.14.1)
- concurrent-ruby (~> 1.0)
- jekyll (3.9.3)
- addressable (~> 2.4)
- colorator (~> 1.0)
- em-websocket (~> 0.5)
- i18n (>= 0.7, < 2)
- jekyll-sass-converter (~> 1.0)
- jekyll-watch (~> 2.0)
- kramdown (>= 1.17, < 3)
- liquid (~> 4.0)
- mercenary (~> 0.3.3)
- pathutil (~> 0.9)
- rouge (>= 1.7, < 4)
- safe_yaml (~> 1.0)
- jekyll-avatar (0.7.0)
- jekyll (>= 3.0, < 5.0)
- jekyll-coffeescript (1.1.1)
- coffee-script (~> 2.2)
- coffee-script-source (~> 1.11.1)
- jekyll-commonmark (1.4.0)
- commonmarker (~> 0.22)
- jekyll-commonmark-ghpages (0.4.0)
- commonmarker (~> 0.23.7)
- jekyll (~> 3.9.0)
- jekyll-commonmark (~> 1.4.0)
- rouge (>= 2.0, < 5.0)
- jekyll-default-layout (0.1.4)
- jekyll (~> 3.0)
- jekyll-feed (0.15.1)
- jekyll (>= 3.7, < 5.0)
- jekyll-gist (1.5.0)
- octokit (~> 4.2)
- jekyll-github-metadata (2.13.0)
- jekyll (>= 3.4, < 5.0)
- octokit (~> 4.0, != 4.4.0)
- jekyll-include-cache (0.2.1)
- jekyll (>= 3.7, < 5.0)
- jekyll-mentions (1.6.0)
- html-pipeline (~> 2.3)
- jekyll (>= 3.7, < 5.0)
- jekyll-optional-front-matter (0.3.2)
- jekyll (>= 3.0, < 5.0)
- jekyll-paginate (1.1.0)
- jekyll-readme-index (0.3.0)
- jekyll (>= 3.0, < 5.0)
- jekyll-redirect-from (0.16.0)
- jekyll (>= 3.3, < 5.0)
- jekyll-relative-links (0.6.1)
- jekyll (>= 3.3, < 5.0)
- jekyll-remote-theme (0.4.3)
- addressable (~> 2.0)
- jekyll (>= 3.5, < 5.0)
- jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0)
- rubyzip (>= 1.3.0, < 3.0)
- jekyll-sass-converter (1.5.2)
- sass (~> 3.4)
- jekyll-seo-tag (2.8.0)
- jekyll (>= 3.8, < 5.0)
- jekyll-sitemap (1.4.0)
- jekyll (>= 3.7, < 5.0)
- jekyll-swiss (1.0.0)
- jekyll-theme-architect (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-cayman (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-dinky (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-hacker (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-leap-day (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-merlot (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-midnight (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-minimal (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-modernist (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-primer (0.6.0)
- jekyll (> 3.5, < 5.0)
- jekyll-github-metadata (~> 2.9)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-slate (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-tactile (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-theme-time-machine (0.2.0)
- jekyll (> 3.5, < 5.0)
- jekyll-seo-tag (~> 2.0)
- jekyll-titles-from-headings (0.5.3)
- jekyll (>= 3.3, < 5.0)
- jekyll-watch (2.2.1)
- listen (~> 3.0)
- jemoji (0.12.0)
- gemoji (~> 3.0)
- html-pipeline (~> 2.2)
- jekyll (>= 3.0, < 5.0)
- kramdown (2.3.2)
- rexml
- kramdown-parser-gfm (1.1.0)
- kramdown (~> 2.0)
- liquid (4.0.4)
- listen (3.8.0)
- rb-fsevent (~> 0.10, >= 0.10.3)
- rb-inotify (~> 0.9, >= 0.9.10)
- mercenary (0.3.6)
- minima (2.5.1)
- jekyll (>= 3.5, < 5.0)
- jekyll-feed (~> 0.9)
- jekyll-seo-tag (~> 2.1)
- minitest (5.20.0)
- mutex_m (0.2.0)
- nokogiri (1.15.5-x86_64-linux)
- racc (~> 1.4)
- octokit (4.25.1)
- faraday (>= 1, < 3)
- sawyer (~> 0.9)
- pathutil (0.16.2)
- forwardable-extended (~> 2.6)
- public_suffix (4.0.7)
- racc (1.7.3)
- rb-fsevent (0.11.2)
- rb-inotify (0.10.1)
- ffi (~> 1.0)
- rexml (3.2.6)
- rouge (3.26.0)
- ruby2_keywords (0.0.5)
- rubyzip (2.3.2)
- safe_yaml (1.0.5)
- sass (3.7.4)
- listen (~> 3.0)
- sawyer (0.9.2)
- addressable (>= 2.3.5)
- faraday (>= 0.17.3, < 3)
- simpleidn (0.2.1)
- unf (~> 0.1.4)
- terminal-table (1.8.0)
- unicode-display_width (~> 1.1, >= 1.1.1)
- typhoeus (1.4.1)
- ethon (>= 0.9.0)
- tzinfo (2.0.6)
- concurrent-ruby (~> 1.0)
- unf (0.1.4)
- unf_ext
- unf_ext (0.0.9.1)
- unicode-display_width (1.8.0)
- webrick (1.8.1)
-
-PLATFORMS
- x86_64-linux
-
-DEPENDENCIES
- github-pages (~> 228)
- jekyll (~> 3.9.0)
- jekyll-feed (~> 0.6)
- kramdown-parser-gfm
- minima (~> 2.0)
- tzinfo (~> 1.2)
- tzinfo-data
- wdm (~> 0.1.0)
- webrick (~> 1.8)
-
-BUNDLED WITH
- 2.4.22
diff --git a/LICENSE-astro-paper b/LICENSE-astro-paper
new file mode 100644
index 0000000..fa7c791
--- /dev/null
+++ b/LICENSE-astro-paper
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 Sat Naing
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 96a8097..87a765e 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,46 @@
# seanrleach.co.uk
-Personal site and technical blog, built with Jekyll and served by GitHub Pages.
+Personal site and technical blog. Built with [Astro](https://astro.build/) and the
+[AstroPaper](https://github.com/satnaing/astro-paper) theme (v7.1.0, MIT, see
+`LICENSE-astro-paper`), and deployed to GitHub Pages by `.github/workflows/deploy.yml`.
## Run locally
+Needs Node 22.23 or newer and pnpm.
+
```bash
-bundle install
-bundle exec jekyll serve
+pnpm install
+pnpm dev # http://localhost:4321
+pnpm build # type-check, build to dist/ and index for search
```
+Search only works after a `pnpm build`, which generates the index.
+
+## Where things are
+
+| What | Where |
+|---|---|
+| Site title, description, socials, feature flags | `astro-paper.config.ts` |
+| Home page intro | `src/pages/index.astro` |
+| About page | `src/content/pages/about.md` |
+| Posts | `src/content/posts/` |
+| Static files served as-is (CNAME, the spa decoder, legacy redirects) | `public/` |
+
## Writing a post
-Add a Markdown file to `_posts/` named `YYYY-MM-DD-title.markdown`:
+Add a Markdown file to `src/content/posts/`. The file name becomes the URL:
+`my-post.md` is served at `/posts/my-post/`.
```yaml
---
-layout: post
title: "Post title"
-description: "One-line summary shown in post lists."
-date: 2023-08-15 22:54:12 +0000
-categories: one two
+description: "One-line summary shown in post lists and search results."
+pubDatetime: 2026-11-09T12:00:00Z
+tags:
+ - one
+ - two
---
```
+
+Add `featured: true` to pin a post to the top of the home page, or `draft: true` to keep it
+out of the build.
diff --git a/_config.yml b/_config.yml
deleted file mode 100644
index 97dde14..0000000
--- a/_config.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-# Site settings
-title: Sean Leach
-tagline: Fractional CTO and engineering lead
-description: >-
- Fractional CTO and engineering lead. I help startups and scaleups move
- faster and get to revenue. Technical writing on the things I build.
-baseurl: ""
-url: "https://seanrleach.co.uk"
-lang: en-GB
-author:
- name: Sean Leach
-github_username: srleach
-
-# Build settings
-markdown: kramdown
-plugins:
- - jekyll-feed
- - jekyll-seo-tag
- - jekyll-sitemap
-
-exclude:
- - README.md
- - Gemfile
- - Gemfile.lock
- - vendor/
diff --git a/_includes/post-list.html b/_includes/post-list.html
deleted file mode 100644
index 822e633..0000000
--- a/_includes/post-list.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
- {%- for post in include.posts %}
-
- {{ post.date | date: '%-d %b %Y' }}
-
-
- {%- endfor %}
-
diff --git a/_layouts/default.html b/_layouts/default.html
deleted file mode 100644
index 17dbf4e..0000000
--- a/_layouts/default.html
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
- {% seo %}
- {% feed_meta %}
-
-
-
-
-
-
- {{ content }}
-
-
-
- {%- for script in page.scripts %}
-
- {%- endfor %}
-
-
diff --git a/_layouts/page.html b/_layouts/page.html
deleted file mode 100644
index c781294..0000000
--- a/_layouts/page.html
+++ /dev/null
@@ -1,9 +0,0 @@
----
-layout: default
----
-
- {{ page.title | escape }}
-
- {{ content }}
-
-
diff --git a/_layouts/post.html b/_layouts/post.html
deleted file mode 100644
index 4a84da6..0000000
--- a/_layouts/post.html
+++ /dev/null
@@ -1,18 +0,0 @@
----
-layout: default
----
-
-
-
- {{ content }}
-
- ← All posts
-
diff --git a/assets/css/main.css b/assets/css/main.css
deleted file mode 100644
index 9ef5068..0000000
--- a/assets/css/main.css
+++ /dev/null
@@ -1,517 +0,0 @@
-:root {
- --bg: #fcfcfb;
- --surface: #f3f3f0;
- --text: #1a1c1e;
- --muted: #5f666d;
- --border: #e2e2dd;
- --accent: #0b5cad;
- --accent-text: #ffffff;
- --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
- --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
- --measure: 44rem;
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- --bg: #111315;
- --surface: #1a1d20;
- --text: #e8e9ea;
- --muted: #9aa2aa;
- --border: #2a2e33;
- --accent: #6cb2f5;
- --accent-text: #0b1016;
- }
-}
-
-*,
-*::before,
-*::after {
- box-sizing: border-box;
-}
-
-html {
- -webkit-text-size-adjust: 100%;
-}
-
-body {
- margin: 0;
- min-height: 100vh;
- display: flex;
- flex-direction: column;
- background: var(--bg);
- color: var(--text);
- font-family: var(--sans);
- font-size: 1.0625rem;
- line-height: 1.65;
-}
-
-main {
- flex: 1;
- width: 100%;
- padding-top: 3.5rem;
- padding-bottom: 4rem;
-}
-
-.wrap {
- max-width: var(--measure);
- margin: 0 auto;
- padding-left: 1.25rem;
- padding-right: 1.25rem;
-}
-
-a {
- color: var(--accent);
- text-decoration-thickness: 1px;
- text-underline-offset: 0.2em;
-}
-
-a:hover {
- text-decoration-thickness: 2px;
-}
-
-h1,
-h2,
-h3 {
- line-height: 1.2;
- letter-spacing: -0.015em;
- text-wrap: balance;
-}
-
-h1 {
- font-size: clamp(1.9rem, 5vw, 2.6rem);
- margin: 0 0 1rem;
-}
-
-h2 {
- font-size: 1.35rem;
- margin: 0 0 1rem;
-}
-
-h3 {
- font-size: 1.05rem;
- margin: 0 0 0.35rem;
-}
-
-p {
- margin: 0 0 1.1rem;
-}
-
-/* Header and footer */
-
-.site-header {
- border-bottom: 1px solid var(--border);
-}
-
-.site-header .wrap,
-.site-footer .wrap {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- justify-content: space-between;
- gap: 0.5rem 1.5rem;
-}
-
-.site-header .wrap {
- padding-top: 1rem;
- padding-bottom: 1rem;
-}
-
-.site-title {
- color: var(--text);
- font-weight: 650;
- text-decoration: none;
-}
-
-.site-nav,
-.site-footer-links {
- display: flex;
- gap: 1.25rem;
-}
-
-.site-nav a {
- color: var(--muted);
- text-decoration: none;
-}
-
-.site-nav a:hover,
-.site-nav a[aria-current="page"] {
- color: var(--text);
-}
-
-.site-footer {
- border-top: 1px solid var(--border);
- color: var(--muted);
- font-size: 0.9rem;
-}
-
-.site-footer .wrap {
- padding-top: 1.25rem;
- padding-bottom: 1.25rem;
-}
-
-.site-footer a {
- color: var(--muted);
-}
-
-/* Shared pieces */
-
-.eyebrow,
-.meta,
-.post-list time {
- color: var(--muted);
- font-family: var(--mono);
- font-size: 0.82rem;
-}
-
-.eyebrow {
- margin-bottom: 0.75rem;
- text-transform: uppercase;
- letter-spacing: 0.08em;
-}
-
-.lede {
- color: var(--muted);
- font-size: 1.2rem;
- line-height: 1.55;
-}
-
-.section {
- margin-top: 3.5rem;
- padding-top: 2.5rem;
- border-top: 1px solid var(--border);
-}
-
-.section-head {
- display: flex;
- align-items: baseline;
- justify-content: space-between;
- gap: 1rem;
-}
-
-/* Home */
-
-.hero h1 {
- font-size: clamp(2.1rem, 6vw, 3.1rem);
-}
-
-.actions {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- gap: 1rem 1.5rem;
- margin: 1.75rem 0 0;
-}
-
-.button {
- display: inline-block;
- padding: 0.6rem 1.1rem;
- border-radius: 6px;
- background: var(--accent);
- color: var(--accent-text);
- font-weight: 600;
- text-decoration: none;
-}
-
-.button:hover {
- filter: brightness(1.1);
-}
-
-.grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
- gap: 1.5rem 2rem;
-}
-
-.grid p {
- margin: 0;
- color: var(--muted);
- font-size: 0.97rem;
-}
-
-/* Post list */
-
-.post-list {
- list-style: none;
- margin: 1.5rem 0 0;
- padding: 0;
-}
-
-.post-list li {
- display: grid;
- grid-template-columns: 7rem 1fr;
- gap: 0.25rem 1rem;
- align-items: baseline;
- padding: 1rem 0;
- border-top: 1px solid var(--border);
-}
-
-.section .post-list {
- margin-top: 0;
-}
-
-.section .post-list li:first-child {
- border-top: 0;
-}
-
-.post-link {
- font-weight: 600;
-}
-
-.post-list p {
- margin: 0.2rem 0 0;
- color: var(--muted);
- font-size: 0.97rem;
-}
-
-@media (max-width: 32rem) {
- .post-list li {
- grid-template-columns: 1fr;
- }
-}
-
-/* Posts and pages */
-
-.post-header {
- margin-bottom: 2rem;
-}
-
-.meta {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- gap: 0.5rem;
- margin-bottom: 0.75rem;
-}
-
-.tag {
- padding: 0.05rem 0.45rem;
- border: 1px solid var(--border);
- border-radius: 999px;
- font-size: 0.75rem;
-}
-
-.post-back {
- margin: 3rem 0 0;
- padding-top: 1.5rem;
- border-top: 1px solid var(--border);
-}
-
-.prose h2 {
- margin-top: 2.5rem;
-}
-
-.prose h3,
-.prose h4 {
- margin: 2rem 0 0.5rem;
-}
-
-.prose ul,
-.prose ol {
- margin: 0 0 1.1rem;
- padding-left: 1.25rem;
-}
-
-.prose li {
- margin-bottom: 0.4rem;
-}
-
-.prose img {
- max-width: 100%;
- height: auto;
- border-radius: 6px;
-}
-
-.prose blockquote {
- margin: 0 0 1.1rem;
- padding-left: 1rem;
- border-left: 3px solid var(--border);
- color: var(--muted);
-}
-
-.prose table {
- display: block;
- overflow-x: auto;
- border-collapse: collapse;
- margin-bottom: 1.1rem;
-}
-
-.prose th,
-.prose td {
- padding: 0.4rem 0.75rem;
- border: 1px solid var(--border);
- text-align: left;
-}
-
-code,
-pre {
- font-family: var(--mono);
- font-size: 0.88em;
-}
-
-code {
- padding: 0.1em 0.35em;
- border-radius: 4px;
- background: var(--surface);
-}
-
-pre {
- margin: 0 0 1.1rem;
- padding: 1rem;
- overflow-x: auto;
- border: 1px solid var(--border);
- border-radius: 6px;
- background: var(--surface);
- line-height: 1.5;
-}
-
-pre code {
- padding: 0;
- background: none;
- font-size: inherit;
-}
-
-/* Rouge syntax highlighting */
-
-.highlight .c,
-.highlight .c1,
-.highlight .cm,
-.highlight .cs {
- color: var(--muted);
- font-style: italic;
-}
-
-.highlight .k,
-.highlight .kd,
-.highlight .kn,
-.highlight .kr,
-.highlight .kt,
-.highlight .nt {
- color: var(--accent);
-}
-
-.highlight .s,
-.highlight .s1,
-.highlight .s2,
-.highlight .sb,
-.highlight .mi,
-.highlight .mf,
-.highlight .mh {
- color: #a8571a;
-}
-
-@media (prefers-color-scheme: dark) {
- .highlight .s,
- .highlight .s1,
- .highlight .s2,
- .highlight .sb,
- .highlight .mi,
- .highlight .mf,
- .highlight .mh {
- color: #e0a372;
- }
-}
-
-/* Interactive decoder (assets/js/spa-decoder.js) */
-
-.decoder {
- margin: 0 0 1.5rem;
- padding: 1rem;
- border: 1px solid var(--border);
- border-radius: 6px;
- background: var(--surface);
-}
-
-.decoder-samples {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- gap: 0.5rem;
- margin-bottom: 0.75rem;
-}
-
-.decoder-label,
-.decoder-unit {
- color: var(--muted);
- font-size: 0.9rem;
-}
-
-.decoder button,
-.decoder select {
- padding: 0.3rem 0.65rem;
- border: 1px solid var(--border);
- border-radius: 6px;
- background: var(--bg);
- color: var(--text);
- font: inherit;
- font-size: 0.85rem;
-}
-
-.decoder button {
- cursor: pointer;
-}
-
-.decoder button:hover {
- border-color: var(--accent);
-}
-
-.decoder-input {
- display: block;
- width: 100%;
- margin-bottom: 0.75rem;
- padding: 0.6rem;
- border: 1px solid var(--border);
- border-radius: 6px;
- background: var(--bg);
- color: var(--text);
- font-family: var(--mono);
- font-size: 0.82rem;
- line-height: 1.5;
- resize: vertical;
-}
-
-.decoder-output {
- margin-top: 0.75rem;
-}
-
-.decoder-frame {
- margin-top: 0.75rem;
- padding: 0.75rem;
- border: 1px solid var(--border);
- border-radius: 6px;
- background: var(--bg);
-}
-
-.decoder-frame-title {
- margin: 0;
- font-weight: 600;
-}
-
-.decoder-frame dl {
- display: grid;
- grid-template-columns: 8.5rem 1fr;
- gap: 0.2rem 1rem;
- margin: 0.5rem 0 0;
- font-size: 0.92rem;
-}
-
-.decoder-frame dt {
- color: var(--muted);
-}
-
-.decoder-frame dd {
- margin: 0;
- font-family: var(--mono);
- font-size: 0.88em;
- overflow-wrap: anywhere;
-}
-
-.decoder-empty {
- margin: 0;
- color: var(--muted);
-}
-
-@media (max-width: 32rem) {
- .decoder-frame dl {
- grid-template-columns: 1fr;
- }
-}
diff --git a/astro-paper.config.ts b/astro-paper.config.ts
new file mode 100644
index 0000000..7c9fed6
--- /dev/null
+++ b/astro-paper.config.ts
@@ -0,0 +1,33 @@
+import { defineAstroPaperConfig } from "./src/types/config";
+
+export default defineAstroPaperConfig({
+ site: {
+ url: "https://seanrleach.co.uk/",
+ title: "Sean Leach",
+ description:
+ "Fractional CTO and engineering lead. I help startups and scaleups move faster and get to revenue. Technical writing on the things I build.",
+ author: "Sean Leach",
+ profile: "https://seanrleach.co.uk/about/",
+ lang: "en",
+ timezone: "Europe/London",
+ dir: "ltr",
+ },
+ posts: {
+ perPage: 10,
+ perIndex: 5,
+ },
+ features: {
+ lightAndDarkMode: true,
+ dynamicOgImage: true,
+ showArchives: true,
+ showBackButton: true,
+ editPost: { enabled: false },
+ search: "pagefind",
+ toc: true,
+ },
+ socials: [{ name: "github", url: "https://github.com/srleach" }],
+ shareLinks: [
+ { name: "x", url: "https://x.com/intent/post?url=" },
+ { name: "mail", url: "mailto:?subject=See%20this%20post&body=" },
+ ],
+});
diff --git a/astro.config.ts b/astro.config.ts
new file mode 100644
index 0000000..6e22bae
--- /dev/null
+++ b/astro.config.ts
@@ -0,0 +1,92 @@
+import {
+ defineConfig,
+ envField,
+ fontProviders,
+ svgoOptimizer,
+} from "astro/config";
+import tailwindcss from "@tailwindcss/vite";
+import mdx from "@astrojs/mdx";
+import sitemap from "@astrojs/sitemap";
+import { rehypeHeadingIds, unified } from "@astrojs/markdown-remark";
+import rehypeCallouts from "rehype-callouts";
+import {
+ transformerNotationDiff,
+ transformerNotationHighlight,
+ transformerNotationWordHighlight,
+} from "@shikijs/transformers";
+import { transformerFileName } from "./src/utils/transformers/fileName";
+import { rehypeTableWrapper } from "./src/utils/rehypeTableWrapper";
+import {
+ inlineTocDeprecation,
+ rehypeInlineToc,
+} from "./src/utils/rehypeInlineToc";
+import config from "./astro-paper.config";
+
+export default defineConfig({
+ site: config.site.url,
+ integrations: [
+ mdx(),
+ sitemap({
+ filter: page =>
+ config.features?.showArchives !== false || !/\/archives\/?$/.test(page),
+ }),
+ inlineTocDeprecation(),
+ ],
+ i18n: {
+ locales: ["en"],
+ defaultLocale: "en",
+ routing: {
+ prefixDefaultLocale: false,
+ },
+ },
+ markdown: {
+ // Deprecated: the theme no longer uses remark-toc or remark-collapse. They
+ // stay in package.json so a customised config importing them still builds,
+ // and are removed in v8.
+ processor: unified({
+ rehypePlugins: [
+ rehypeHeadingIds,
+ rehypeInlineToc,
+ rehypeCallouts,
+ rehypeTableWrapper,
+ ],
+ }),
+ shikiConfig: {
+ themes: { light: "min-light", dark: "night-owl" },
+ defaultColor: false,
+ wrap: false,
+ transformers: [
+ transformerFileName({ style: "v2", hideDot: false }),
+ transformerNotationHighlight(),
+ transformerNotationWordHighlight(),
+ transformerNotationDiff({ matchAlgorithm: "v3" }),
+ ],
+ },
+ },
+ vite: {
+ plugins: [tailwindcss()],
+ },
+ fonts: [
+ {
+ name: "Google Sans Code",
+ cssVariable: "--font-google-sans-code",
+ provider: fontProviders.google(),
+ fallbacks: ["monospace"],
+ weights: [300, 400, 500, 600, 700],
+ styles: ["normal", "italic"],
+ formats: ["woff", "ttf"],
+ },
+ ],
+ env: {
+ schema: {
+ PUBLIC_GOOGLE_SITE_VERIFICATION: envField.string({
+ access: "public",
+ context: "client",
+ optional: true,
+ }),
+ },
+ },
+ experimental: {
+ svgOptimizer: svgoOptimizer(),
+ },
+});
diff --git a/blog/index.html b/blog/index.html
deleted file mode 100644
index 03a091c..0000000
--- a/blog/index.html
+++ /dev/null
@@ -1,11 +0,0 @@
----
-layout: default
-title: Blog
-description: Technical writing on software, hardware and engineering leadership.
-permalink: /blog/
----
-
- Blog
- Technical notes on things I've built, broken and fixed.
- {% include post-list.html posts=site.posts %}
-
diff --git a/copy-pagefind.mjs b/copy-pagefind.mjs
new file mode 100644
index 0000000..1358f7d
--- /dev/null
+++ b/copy-pagefind.mjs
@@ -0,0 +1,7 @@
+// Cross-platform stand-in for the previous `cp -r dist/pagefind public/`
+// build step: cmd.exe, the shell npm/pnpm runs scripts with on Windows,
+// has no `cp`, so the full build used to fail there. Keeping the index in
+// public/ lets `astro dev` and subsequent builds serve it.
+import { cpSync } from "node:fs";
+
+cpSync("dist/pagefind", "public/pagefind", { recursive: true, force: true });
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 0000000..0fe42ad
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,22 @@
+import eslintPluginAstro from "eslint-plugin-astro";
+import tsParser from "@typescript-eslint/parser";
+
+export default [
+ ...eslintPluginAstro.configs.recommended,
+ {
+ files: ["**/*.astro"],
+ languageOptions: {
+ parserOptions: {
+ parser: tsParser,
+ },
+ },
+ },
+ {
+ files: ["**/*.ts", "**/*.tsx"],
+ languageOptions: {
+ parser: tsParser,
+ },
+ },
+ { rules: { "no-console": "error" } },
+ { ignores: ["dist/**", ".astro/**", "public/pagefind/**"] },
+];
diff --git a/index.html b/index.html
deleted file mode 100644
index 7e74555..0000000
--- a/index.html
+++ /dev/null
@@ -1,43 +0,0 @@
----
-layout: default
----
-
- Sean Leach
- Fractional CTO and engineering lead for startups and scaleups.
-
- I help companies build faster and get to revenue: setting technical
- direction, building and leading the engineering team, and shipping
- alongside them.
-
-
- What I do
- Read the blog →
-
-
-
-
- How I help
-
-
-
Fractional CTO
-
Senior technical leadership without the full-time hire. Architecture, hiring, roadmap and the technical side of the board conversation.
-
-
-
Engineering lead
-
Hands-on leadership for teams that need to deliver. I unblock the work, tighten the process and raise the pace.
-
-
-
Getting to revenue
-
Cut scope to what customers will pay for, ship it, and build only what the next stage needs.
-
-
-
-
-
-
- {% assign recent = site.posts | slice: 0, 5 %}
- {% include post-list.html posts=recent %}
-
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..7dd8cc1
--- /dev/null
+++ b/package.json
@@ -0,0 +1,50 @@
+{
+ "name": "srleach-github-io",
+ "type": "module",
+ "private": true,
+ "engines": {
+ "node": ">=22.23.4"
+ },
+ "scripts": {
+ "dev": "astro dev",
+ "build": "astro check && astro build && pagefind --site dist && node copy-pagefind.mjs",
+ "preview": "astro preview",
+ "sync": "astro sync",
+ "astro": "astro",
+ "format:check": "prettier --check .",
+ "format": "prettier --write .",
+ "lint": "eslint ."
+ },
+ "dependencies": {
+ "@astrojs/markdown-remark": "^7.3.5",
+ "@astrojs/mdx": "^7.0.8",
+ "@astrojs/rss": "^4.0.19",
+ "@astrojs/sitemap": "^3.7.4",
+ "@tailwindcss/vite": "^4.3.3",
+ "astro": "^7.4.5",
+ "dayjs": "^1.11.23",
+ "lodash.kebabcase": "^4.1.1",
+ "rehype-callouts": "^2.2.0",
+ "remark-collapse": "^0.1.2",
+ "remark-toc": "^9.0.0",
+ "satori": "^0.63.2",
+ "sharp": "^0.35.5",
+ "slugify": "^1.6.9",
+ "tailwindcss": "^4.3.3"
+ },
+ "devDependencies": {
+ "@astrojs/check": "^0.9.12",
+ "@pagefind/default-ui": "^1.6.0",
+ "@shikijs/transformers": "^4.6.0",
+ "@tailwindcss/typography": "^0.5.20",
+ "@types/lodash.kebabcase": "^4.1.9",
+ "@typescript-eslint/parser": "^8.72.1",
+ "eslint": "^10.14.0",
+ "eslint-plugin-astro": "^3.2.1",
+ "pagefind": "^1.6.0",
+ "prettier": "3.9.10",
+ "prettier-plugin-astro": "0.14.1",
+ "prettier-plugin-tailwindcss": "^0.8.1",
+ "typescript": "^6.0.3"
+ }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..db2ea43
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,6129 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@astrojs/markdown-remark':
+ specifier: ^7.3.5
+ version: 7.3.5
+ '@astrojs/mdx':
+ specifier: ^7.0.8
+ version: 7.0.8(astro@7.4.5(@astrojs/markdown-remark@7.3.5)(@types/node@24.19.2)(jiti@2.7.0)(yaml@2.9.1))
+ '@astrojs/rss':
+ specifier: ^4.0.19
+ version: 4.0.19
+ '@astrojs/sitemap':
+ specifier: ^3.7.4
+ version: 3.7.4
+ '@tailwindcss/vite':
+ specifier: ^4.3.3
+ version: 4.3.3(vite@8.3.5(@types/node@24.19.2)(jiti@2.7.0)(yaml@2.9.1))
+ astro:
+ specifier: ^7.4.5
+ version: 7.4.5(@astrojs/markdown-remark@7.3.5)(@types/node@24.19.2)(jiti@2.7.0)(yaml@2.9.1)
+ dayjs:
+ specifier: ^1.11.23
+ version: 1.11.23
+ lodash.kebabcase:
+ specifier: ^4.1.1
+ version: 4.1.1
+ rehype-callouts:
+ specifier: ^2.2.0
+ version: 2.2.0
+ remark-collapse:
+ specifier: ^0.1.2
+ version: 0.1.2
+ remark-toc:
+ specifier: ^9.0.0
+ version: 9.0.0
+ satori:
+ specifier: ^0.63.2
+ version: 0.63.2
+ sharp:
+ specifier: ^0.35.5
+ version: 0.35.5(@types/node@24.19.2)
+ slugify:
+ specifier: ^1.6.9
+ version: 1.6.9
+ tailwindcss:
+ specifier: ^4.3.3
+ version: 4.3.3
+ devDependencies:
+ '@astrojs/check':
+ specifier: ^0.9.12
+ version: 0.9.12(@emnapi/runtime@1.11.3)(prettier-plugin-astro@0.14.1)(prettier@3.9.10)(typescript@6.0.3)
+ '@pagefind/default-ui':
+ specifier: ^1.6.0
+ version: 1.6.0
+ '@shikijs/transformers':
+ specifier: ^4.6.0
+ version: 4.6.0
+ '@tailwindcss/typography':
+ specifier: ^0.5.20
+ version: 0.5.20(tailwindcss@4.3.3)
+ '@types/lodash.kebabcase':
+ specifier: ^4.1.9
+ version: 4.1.9
+ '@typescript-eslint/parser':
+ specifier: ^8.72.1
+ version: 8.72.1(eslint@10.14.0(jiti@2.7.0))(typescript@6.0.3)
+ eslint:
+ specifier: ^10.14.0
+ version: 10.14.0(jiti@2.7.0)
+ eslint-plugin-astro:
+ specifier: ^3.2.1
+ version: 3.2.1(@typescript-eslint/parser@8.72.1(eslint@10.14.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.14.0(jiti@2.7.0))
+ pagefind:
+ specifier: ^1.6.0
+ version: 1.6.0
+ prettier:
+ specifier: 3.9.10
+ version: 3.9.10
+ prettier-plugin-astro:
+ specifier: 0.14.1
+ version: 0.14.1
+ prettier-plugin-tailwindcss:
+ specifier: ^0.8.1
+ version: 0.8.1(prettier-plugin-astro@0.14.1)(prettier@3.9.10)
+ typescript:
+ specifier: ^6.0.3
+ version: 6.0.3
+
+packages:
+
+ '@astrojs/astro2tsx@0.1.3':
+ resolution: {integrity: sha512-GMDC7Eo0myBUA/QLBl0u+OcegJ8LiBhccmNZR34KmprQNP8gQUJlWlOOZWUsxNA2rNS6GuPYcGDyLoPnjmn4/Q==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+
+ '@astrojs/check@0.9.12':
+ resolution: {integrity: sha512-hazpqxIfZWj2VhyPyR0pOEQ/IhFDM/2nIApL96zMXABdD8NrUKYDmhxTy7h+drTDl9MQG/Sd1ET/4amadVJUUA==}
+ hasBin: true
+ peerDependencies:
+ typescript: ^5.0.0 || ^6.0.0
+
+ '@astrojs/compiler-binding-android-arm64@0.5.2':
+ resolution: {integrity: sha512-kckIxon6Rm/Bxzh9mGPYpRRqUbToUQoHptCmpMmE+Yt0Xumq8CqrKUidyYhrBsrHVO7bxuRzzWUUqvpf+iYceQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
+ '@astrojs/compiler-binding-darwin-arm64@0.5.2':
+ resolution: {integrity: sha512-1ZG5Ar3dD8HtodX9XH1mRmE0qBXZlvjYXSFfpgFwocqfWnvhFuqtGUNseL8wgfImiRMztIjpQqlogQFQQtgiag==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@astrojs/compiler-binding-darwin-x64@0.5.2':
+ resolution: {integrity: sha512-+kl35S7hiHJ2cb7eujwEsPFYYuSgE11uEE2Z20lAJ+undpgulMcW+weQqFPOSSZccEKHOiLIY+kQHkb4e4092w==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@astrojs/compiler-binding-linux-arm64-gnu@0.5.2':
+ resolution: {integrity: sha512-BqlR0EHNQLPLwgL9+xogjsAc7zGf0cHho2CPdhv0P1yqbVMwGVaGAqrANP6VYAXCqSC43AF4Czcxc2n+oqdesg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@astrojs/compiler-binding-linux-arm64-musl@0.5.2':
+ resolution: {integrity: sha512-GRGyby3AZWw5d6JQzidyxPyvGgHWsSusrpgKyFVpDTKctABF7L3Pd5V1cRJbV36/rcu+Eh292/wL7pF7MkmGkQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@astrojs/compiler-binding-linux-x64-gnu@0.5.2':
+ resolution: {integrity: sha512-9XAqGTMQAtIslMsjK3xdyY6OI9d59OZqcLBB6pvD/DcoR0daYswfrKx2azdDERDY4AqAnWPC7odVM1BmxYv3Vw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@astrojs/compiler-binding-linux-x64-musl@0.5.2':
+ resolution: {integrity: sha512-jOViewGeTh9bSfS8vK72ECxoy2yvmA+Pd/ZcEvBI5c7+7IcfHwZ5qA4L9fRYuOhSrAifvQ2dV4vm1XAtloaWBw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@astrojs/compiler-binding-wasm32-wasi@0.5.2':
+ resolution: {integrity: sha512-Z7p64doar1uvR5C9Xs6j1CV9lGlPVQX7OQ2aAtZ/xKC5aGS6GB13nyc2UfQXTQLvZrvVwsyaflTQy6ziY5Bekw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [wasm32]
+
+ '@astrojs/compiler-binding-win32-arm64-msvc@0.5.2':
+ resolution: {integrity: sha512-Tr13bh+ahTWsjXjzctK7pFpWV+dUU4FtBfETlR7gO+ueex0F+RDch7eRr0YfKysaRhk9tWm14lsCJ5uWyFPNHQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@astrojs/compiler-binding-win32-x64-msvc@0.5.2':
+ resolution: {integrity: sha512-SfKkI+jOQYLh6wojPvVPV0lFfQnxisweN9B+qCzRvZp7DHAuGif8Y0WcC5PUgN4jb8VKy57LggQDtggR46Du1g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@astrojs/compiler-binding@0.5.2':
+ resolution: {integrity: sha512-lEn76+0JcMQ5RDXU0wSMWJg2ZppiZvSULpghC29cfv4o9bhvWFDbQjsCtvjjXN9iVLFQ7fL3wTR2L2UU13mP8g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+
+ '@astrojs/compiler-rs@0.5.2':
+ resolution: {integrity: sha512-IvwYLB8t1M86rKO4oPdBi9GP4gzXcgrVlNgocjwos1jeeUuEIV8d9Crn1PvYD/5IKteWOxpIHomf7e8iYlkE+Q==}
+ engines: {node: '>=22.12.0'}
+
+ '@astrojs/compiler@2.13.1':
+ resolution: {integrity: sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==}
+
+ '@astrojs/internal-helpers@0.10.4':
+ resolution: {integrity: sha512-nozZSy/mKYLqe4YrqbKtdOszedAfXYCtw3wZ0d+CAjz4GqQ4L9rl1ltIL5BlgwmYVinJg/RZ0MgGuWOdlyRZlA==}
+
+ '@astrojs/internal-helpers@0.13.0':
+ resolution: {integrity: sha512-20jxQqnSmpskcGyad7N5l6Xf1gz0UCa6lNde44bEBdaXGpncEYfAZJ142r2ZgzBaG4dUyzGmhqZWv1INU2eiHQ==}
+
+ '@astrojs/language-server@2.17.3':
+ resolution: {integrity: sha512-QIAFu1e/4quJMTPdmBOm+ZPXc5NDGZE4/YQjgR3EgjhGLl/tdidaWJDr5WmO2lesVc+BW1qN/xNBURmuePDG2A==}
+ hasBin: true
+ peerDependencies:
+ prettier: ^3.0.0
+ prettier-plugin-astro: '>=0.11.0'
+ peerDependenciesMeta:
+ prettier:
+ optional: true
+ prettier-plugin-astro:
+ optional: true
+
+ '@astrojs/markdown-remark@7.2.4':
+ resolution: {integrity: sha512-MvspGMynWKAjTe4/lTUdmBPHIFKNVLTCF6UlyWGogTGzNrTvjD+D4n48k7h8swxsEPKHK2TwxkZO7uoaCv1Pow==}
+
+ '@astrojs/markdown-remark@7.3.5':
+ resolution: {integrity: sha512-Xk0gvOMEpBul2zj5jZXS5WH2Gng/6dW7c3WWLXgip+LZGpY64frwTvBChznMtpnNIqVgFXSsu44RYrISojcodQ==}
+
+ '@astrojs/markdown-satteri@0.4.6':
+ resolution: {integrity: sha512-6biKp2lEAYycF3jhv91br+Otyf4EgDFkF1oRL9ks0u/m/YRGmI7XkGQ2MgaM35DslT03f7NtozKtlqfBNkp6bw==}
+
+ '@astrojs/mdx@7.0.8':
+ resolution: {integrity: sha512-RNuwq2ccTSi7NX9YqlR0noaWoVdOrZuJvdfWXotAzdhMVB0b0zEMLnNU+xar7le0GwTMlTObD/QAu8jeHA/3nA==}
+ engines: {node: '>=22.12.0'}
+ peerDependencies:
+ '@astrojs/markdown-satteri': ^0.3.1
+ astro: ^7.0.0
+ peerDependenciesMeta:
+ '@astrojs/markdown-satteri':
+ optional: true
+
+ '@astrojs/prism@4.0.2':
+ resolution: {integrity: sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==}
+ engines: {node: '>=22.12.0'}
+
+ '@astrojs/prism@4.1.2':
+ resolution: {integrity: sha512-jMrUEpFDarDO1KWPvVc9eSa8DSfwBlwEOHwX+8zvZQpiqXRGDxFnOXHsYqV6DP7w+0qIgTr98gS1GdQlm8ypWg==}
+ engines: {node: '>=22.23.4'}
+
+ '@astrojs/rss@4.0.19':
+ resolution: {integrity: sha512-e+z5wYeYtffQdHQO8c2tkSd2JEBdAuRXJV4ZEU5IxkYeE6e39woDd7nw1PH1Kk2tEYNCYuKdylnnbhGmt61awA==}
+
+ '@astrojs/sitemap@3.7.4':
+ resolution: {integrity: sha512-LbKNC24bdUWcQf/pThB6qLlSqHojxGjZDURIzFocY8rlWnAn2t74nnhnK6S5x0NHriHoAduLEpVjRykmeGiVvA==}
+
+ '@astrojs/telemetry@3.4.2':
+ resolution: {integrity: sha512-2JOMRUxJrBSfiVDfeKLv1wBmvSIx1Y6u2YEO8awZ9qSq3u/rNAiUyFdOYi8dHv91kHrmYEQX7pRDw0J4xyj3UA==}
+ engines: {node: '>=22.23.4'}
+
+ '@astrojs/yaml2ts@0.2.4':
+ resolution: {integrity: sha512-8oddpOae35pJsXPQXhTkM0ypfKPskVsh2bCxRtbf7e+/Epw2nReakFYpLKjZMEr75CsoF203PMnCocpfz0s69A==}
+
+ '@babel/helper-string-parser@7.29.11':
+ resolution: {integrity: sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.29.7':
+ resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.29.11':
+ resolution: {integrity: sha512-OZ6+oaK7GMyg3IfCpj8JOmn8n+w2rJbDT0LLfVDTLb1ZFeKDh3AiNELgCMUw175IJHSkLl74hG0M0VLsvx+gag==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/types@7.29.11':
+ resolution: {integrity: sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@bruits/satteri-darwin-arm64@0.10.5':
+ resolution: {integrity: sha512-27KTVl4TJkVahMy/ohyA7qd4938G5UNneFUz/PsScYfpIhj0IVAS23mpcJXdPF44sa6nva198lmV/cKIb2YPyA==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@bruits/satteri-darwin-x64@0.10.5':
+ resolution: {integrity: sha512-IjnLe3nKspq6qaeqGgjT7MT8VrTV74yWRlaag7ZdNsI8TDAYZ0iPxMCo+9KQZHUk5EyVB+reBI/PFWL5KuFw9Q==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@bruits/satteri-linux-arm64-gnu@0.10.5':
+ resolution: {integrity: sha512-glkYXZCJywjP13v67eAyAMSJdF+ncvEbYvgi/wOtffL9tQ27lr/zsyzUfgs+ovjJ9d8JNQKiXeiArJcX8PJL9w==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@bruits/satteri-linux-arm64-musl@0.10.5':
+ resolution: {integrity: sha512-yWdgG1g17Nh2QyGVlFUxGRa3FEFwiMcpZEyMNWkbM3deC94cmVc+/i9OuyFpdKuWo3GkgoCtYVOoxk1uCnCZIA==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@bruits/satteri-linux-x64-gnu@0.10.5':
+ resolution: {integrity: sha512-FVaLoPT1fBgGl0J+AYebyyXJYBachGl8Oyyrf1lye4RTqCB4S0Gwkj1uM9RJyThUOvx5VUmAT1CnNh1SFHA+kw==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@bruits/satteri-linux-x64-musl@0.10.5':
+ resolution: {integrity: sha512-EHpVAx2bqW3GINHTKkljtxVfQmVDGWIuwOYOP5YghTj+0PkBa2o8oKPRtQ9Kbsr1Fye8jtUcDjhwj2jMNugZKg==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@bruits/satteri-wasm32-wasi@0.10.5':
+ resolution: {integrity: sha512-ypz8c/Zmipxp4IoeDa228Gstv6TLzVmNs3yC6wKCoNSOjx1iwpgzu87Y3hTkXFdwChVGU85qeUDuOIarGUZQLw==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+
+ '@bruits/satteri-win32-arm64-msvc@0.10.5':
+ resolution: {integrity: sha512-siTV88nb0LRqNpkL2gXboqCwVdq95sLtzMHS1/3eONV2gLbB3NAK46wmSMvCO/yquBvI2lvaFIfd8P12ecsxBw==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@bruits/satteri-win32-x64-msvc@0.10.5':
+ resolution: {integrity: sha512-C3IfPvfvMXmlzBxaMPKFS1XiuV9pu2mC7YqkPk7PSvTgPZ8gbdASIpHpztDLvTTQjqZ0z1Ol8tK5X+V6XXC0wQ==}
+ cpu: [x64]
+ os: [win32]
+
+ '@cacheable/memory@2.2.0':
+ resolution: {integrity: sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==}
+
+ '@cacheable/utils@2.5.0':
+ resolution: {integrity: sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==}
+
+ '@capsizecss/unpack@4.0.1':
+ resolution: {integrity: sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==}
+ engines: {node: '>=18'}
+
+ '@clack/core@1.5.1':
+ resolution: {integrity: sha512-iHTrHA8MtVuLl2TfZySmcKv1qO2PoyC9Z7pfSDozEuV5vtY3/wcOPKJXlqJ5Oq2Cx5DDGQGAMVx6HZfRRoVEbQ==}
+ engines: {node: '>= 20.12.0'}
+
+ '@clack/prompts@1.8.1':
+ resolution: {integrity: sha512-dlT1m5e/0yUL0kRNcQn7yGLVThkgbB0Ga/1AmfDDC/8ik6AIiSf2QLQO2zPYvefsHP0aFgxO93cVLCCfDp7kzQ==}
+ engines: {node: '>= 20.12.0'}
+
+ '@emmetio/abbreviation@2.3.3':
+ resolution: {integrity: sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==}
+
+ '@emmetio/css-abbreviation@2.1.8':
+ resolution: {integrity: sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==}
+
+ '@emmetio/css-parser@0.4.1':
+ resolution: {integrity: sha512-2bC6m0MV/voF4CTZiAbG5MWKbq5EBmDPKu9Sb7s7nVcEzNQlrZP6mFFFlIaISM8X6514H9shWMme1fCm8cWAfQ==}
+
+ '@emmetio/html-matcher@1.3.0':
+ resolution: {integrity: sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ==}
+
+ '@emmetio/scanner@1.0.4':
+ resolution: {integrity: sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==}
+
+ '@emmetio/stream-reader-utils@0.1.0':
+ resolution: {integrity: sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A==}
+
+ '@emmetio/stream-reader@2.2.0':
+ resolution: {integrity: sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw==}
+
+ '@emnapi/core@1.11.1':
+ resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==}
+
+ '@emnapi/core@1.11.3':
+ resolution: {integrity: sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==}
+
+ '@emnapi/runtime@1.11.1':
+ resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==}
+
+ '@emnapi/runtime@1.11.3':
+ resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==}
+
+ '@emnapi/wasi-threads@1.2.2':
+ resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==}
+
+ '@emnapi/wasi-threads@1.2.3':
+ resolution: {integrity: sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==}
+
+ '@eslint-community/eslint-utils@4.10.1':
+ resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+ '@eslint-community/regexpp@4.12.2':
+ resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ '@eslint/config-array@0.23.5':
+ resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@eslint/config-helpers@0.7.0':
+ resolution: {integrity: sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@eslint/core@1.2.1':
+ resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@eslint/object-schema@3.0.5':
+ resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@eslint/plugin-kit@0.7.3':
+ resolution: {integrity: sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@humanfs/core@0.19.2':
+ resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanfs/node@0.16.8':
+ resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanfs/types@0.15.0':
+ resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanwhocodes/module-importer@1.0.1':
+ resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+ engines: {node: '>=12.22'}
+
+ '@humanwhocodes/retry@0.4.3':
+ resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
+ engines: {node: '>=18.18'}
+
+ '@img/colour@1.1.0':
+ resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==}
+ engines: {node: '>=18'}
+
+ '@img/sharp-darwin-arm64@0.35.5':
+ resolution: {integrity: sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==}
+ engines: {node: '>=20.9.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-darwin-x64@0.35.5':
+ resolution: {integrity: sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==}
+ engines: {node: '>=20.9.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-freebsd-wasm32@0.35.5':
+ resolution: {integrity: sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==}
+ engines: {node: '>=20.9.0'}
+ os: [freebsd]
+
+ '@img/sharp-libvips-darwin-arm64@1.3.4':
+ resolution: {integrity: sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-x64@1.3.4':
+ resolution: {integrity: sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-linux-arm64@1.3.4':
+ resolution: {integrity: sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-arm@1.3.4':
+ resolution: {integrity: sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-ppc64@1.3.4':
+ resolution: {integrity: sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-riscv64@1.3.4':
+ resolution: {integrity: sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-s390x@1.3.4':
+ resolution: {integrity: sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-x64@1.3.4':
+ resolution: {integrity: sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.3.4':
+ resolution: {integrity: sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-libvips-linuxmusl-x64@1.3.4':
+ resolution: {integrity: sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-linux-arm64@0.35.5':
+ resolution: {integrity: sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==}
+ engines: {node: '>=20.9.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-arm@0.35.5':
+ resolution: {integrity: sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==}
+ engines: {node: '>=20.9.0'}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-ppc64@0.35.5':
+ resolution: {integrity: sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==}
+ engines: {node: '>=20.9.0'}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-riscv64@0.35.5':
+ resolution: {integrity: sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==}
+ engines: {node: '>=20.9.0'}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-s390x@0.35.5':
+ resolution: {integrity: sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==}
+ engines: {node: '>=20.9.0'}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-x64@0.35.5':
+ resolution: {integrity: sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==}
+ engines: {node: '>=20.9.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linuxmusl-arm64@0.35.5':
+ resolution: {integrity: sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==}
+ engines: {node: '>=20.9.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-linuxmusl-x64@0.35.5':
+ resolution: {integrity: sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==}
+ engines: {node: '>=20.9.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-wasm32@0.35.5':
+ resolution: {integrity: sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==}
+ engines: {node: '>=20.9.0'}
+
+ '@img/sharp-webcontainers-wasm32@0.35.5':
+ resolution: {integrity: sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==}
+ engines: {node: '>=20.9.0'}
+ cpu: [wasm32]
+
+ '@img/sharp-win32-arm64@0.35.5':
+ resolution: {integrity: sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==}
+ engines: {node: '>=20.9.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@img/sharp-win32-ia32@0.35.5':
+ resolution: {integrity: sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==}
+ engines: {node: ^20.9.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@img/sharp-win32-x64@0.35.5':
+ resolution: {integrity: sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==}
+ engines: {node: '>=20.9.0'}
+ cpu: [x64]
+ os: [win32]
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.6.0':
+ resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@keyv/bigmap@1.3.1':
+ resolution: {integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==}
+ engines: {node: '>= 18'}
+ peerDependencies:
+ keyv: ^5.6.0
+
+ '@keyv/serialize@1.1.1':
+ resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==}
+
+ '@mdx-js/mdx@3.1.1':
+ resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==}
+
+ '@napi-rs/wasm-runtime@1.2.4':
+ resolution: {integrity: sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0}
+ peerDependencies:
+ '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4
+ '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.4
+
+ '@napi-rs/wasm-runtime@1.2.6':
+ resolution: {integrity: sha512-mRyDmEw9/fCDXCGfL1sbgIw/GUK0PVxRW4YwgPN61F3LQ+fSyoBFarJVzgGyhF6kRzEQZ0FejDqm4WLXSeqQmw==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0}
+ peerDependencies:
+ '@emnapi/core': ^1.7.1 || 2.0.0-beta.2
+ '@emnapi/runtime': ^1.7.1 || 2.0.0-beta.2
+
+ '@nodable/entities@3.1.0':
+ resolution: {integrity: sha512-LsS/DjHr+uDM647Gru/cA8+J3a3HfhttwCKLyuoyN7yXTFCxKBtSgMQBvrW9yNPa2/zDRUNuGPaH5QUFoa4arQ==}
+
+ '@oslojs/encoding@1.1.0':
+ resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==}
+
+ '@oxc-project/types@0.155.0':
+ resolution: {integrity: sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==}
+
+ '@pagefind/darwin-arm64@1.6.0':
+ resolution: {integrity: sha512-611gl64aqN12UcMyKPtZ8+vRWBKlJFSh2JgO8uScxa1Eyjo1dXz9r4wZuRno0yCUE/KN0FauEoKZ3KEwUYHCCg==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@pagefind/darwin-x64@1.6.0':
+ resolution: {integrity: sha512-rd3ZbtuxyYqjASKZAPuIV7eeQQHJ7VYpoNKKG8CvglaD3g30eT5Nb7XGNLYwVReBsCccpkURwrGwWjSwr8nyQQ==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@pagefind/default-ui@1.6.0':
+ resolution: {integrity: sha512-Wj93IzlLnaYYrMpOYrsqUerzA7+Vrs5Ri1U78hBh3zuKHNVkcjApNo5DUM99MzAam6f437VGG64jDenAObIt+w==}
+
+ '@pagefind/freebsd-x64@1.6.0':
+ resolution: {integrity: sha512-d+sXqsaThszmPWGVDSRKqIz9GpZcj/bAksNZfg19asSxU0ogNPOMND4kV4giQvOjPFMsltUFf6hQJfoHQ6+hSQ==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@pagefind/linux-arm64@1.6.0':
+ resolution: {integrity: sha512-SLB2I+XqCZma8NvIGIH8nyKblgKIxw5PmqsVtofWOKxNHhEtOEsddbqU+hsrNcT3EV0faM6UmKDHVIxcPYq29w==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@pagefind/linux-x64@1.6.0':
+ resolution: {integrity: sha512-IbMSx/+Vr/Xk0TTBlLkjgK+rvnbOXGmmb2/MKxQu99AR5DsqQoV3NKEA9PQz2tAhuCZjeD5xJBp4w5N5DHIC7Q==}
+ cpu: [x64]
+ os: [linux]
+
+ '@pagefind/windows-arm64@1.6.0':
+ resolution: {integrity: sha512-wUrniW3REr7VWJHz6xE6F/p/AQgHDFpGZiY9sbWT71KfEijDE5Jnc37V8zc7cilV9h/QkwJGXoEP2+ZLwDmhtw==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@pagefind/windows-x64@1.6.0':
+ resolution: {integrity: sha512-cUA2+yXfbymdJllyMjY5YTBwuOgh0/H53Pn5fYKjDnDjk154Ft4U5RVj3TenWySPL/jG6zJfARUWSlyUyXHlZw==}
+ cpu: [x64]
+ os: [win32]
+
+ '@pkgr/core@0.3.6':
+ resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+
+ '@rolldown/binding-android-arm-eabi@1.2.15':
+ resolution: {integrity: sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [android]
+
+ '@rolldown/binding-android-arm64@1.2.15':
+ resolution: {integrity: sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
+ '@rolldown/binding-darwin-arm64@1.2.15':
+ resolution: {integrity: sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rolldown/binding-darwin-x64@1.2.15':
+ resolution: {integrity: sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rolldown/binding-freebsd-x64@1.2.15':
+ resolution: {integrity: sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rolldown/binding-linux-arm-gnueabihf@1.2.15':
+ resolution: {integrity: sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@rolldown/binding-linux-arm64-gnu@1.2.15':
+ resolution: {integrity: sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-arm64-musl@1.2.15':
+ resolution: {integrity: sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@rolldown/binding-linux-ppc64-gnu@1.2.15':
+ resolution: {integrity: sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-s390x-gnu@1.2.15':
+ resolution: {integrity: sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-x64-gnu@1.2.15':
+ resolution: {integrity: sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-x64-musl@1.2.15':
+ resolution: {integrity: sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@rolldown/binding-openharmony-arm64@1.2.15':
+ resolution: {integrity: sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rolldown/binding-win32-arm64-msvc@1.2.15':
+ resolution: {integrity: sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rolldown/binding-win32-x64-msvc@1.2.15':
+ resolution: {integrity: sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@rolldown/pluginutils@1.0.1':
+ resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
+
+ '@shikijs/core@4.5.0':
+ resolution: {integrity: sha512-6CIgwq+bt2JZ3OSjb/nBkkwEf/CfpHcbam+Mg5Pllco7HAGRQ2/EFhZrrJtbyNTgKwknfVS+llsXGygiDGwJWw==}
+ engines: {node: '>=20'}
+
+ '@shikijs/core@4.6.0':
+ resolution: {integrity: sha512-bfd4OKgvoyXYiYmMRuleA656P4V7M/Du9lcTl38Ngm6KszMj7bq4J1IGZwSYR4c95Tv5UQALtYNWUwaI9UT3xw==}
+ engines: {node: '>=20'}
+
+ '@shikijs/engine-javascript@4.5.0':
+ resolution: {integrity: sha512-Ed6UdEj84LyYkwnxM5CvSMWAD+id9ExTYri0/+x41U9zrgNifPSS9lZm0JqBP0VfYI8yr8tY5nbNq9+p9R0kFA==}
+ engines: {node: '>=20'}
+
+ '@shikijs/engine-oniguruma@4.5.0':
+ resolution: {integrity: sha512-dKVmbJecVdB9recl8a/CU4jdpNXyjJQrIcesIkpS70s5HS02ohpnCSpMUbotPnEUzbAcLQ+S4hwZYbkBBiTcJw==}
+ engines: {node: '>=20'}
+
+ '@shikijs/langs@4.5.0':
+ resolution: {integrity: sha512-wwBTqwduXSz0X4v9Qa2EWeFaYJEw1clCncWjoRNQRbVDu3SPRTRWucnouYapKj3b8Q+sk/41Vbc4B5c8nuYzLA==}
+ engines: {node: '>=20'}
+
+ '@shikijs/primitive@4.5.0':
+ resolution: {integrity: sha512-UM7ShlA8oz0OWxLLO8BmO9Ip6fVqdkt8yF0EH7nOCqHfrx1eEAzF0ZUVK7L+I8NEgeu1PEWSRlxaiPPeCb4iDg==}
+ engines: {node: '>=20'}
+
+ '@shikijs/primitive@4.6.0':
+ resolution: {integrity: sha512-c54+TQEo9ToqgfL/AU8FpjnV09+GA9Oa7M5ROkkXteZfp5XzpAJGXjjvZQFQioJOwm/w/6Z3lIzQstAkl8coag==}
+ engines: {node: '>=20'}
+
+ '@shikijs/themes@4.5.0':
+ resolution: {integrity: sha512-zGb87H3UBbpuI4yoh5UguB1XbU+2BDU3kflM7UfZ+Cy2hWo9lBtqjSVK20gmLY6ukYl040hAypGXMgud9vul8Q==}
+ engines: {node: '>=20'}
+
+ '@shikijs/transformers@4.6.0':
+ resolution: {integrity: sha512-oZ28gYlPOYspv6AD74lCb14AAJugSY/+nJxRWjH42yDbdLAdftVU/PP36tOhb435Sfop2UGWcRGXY7HLLveKCg==}
+ engines: {node: '>=20'}
+
+ '@shikijs/types@4.5.0':
+ resolution: {integrity: sha512-drtPrtD8N0DENyD2cyt6Ajy+IWsiCLgMxODwLTpvH1UsN6a9jf90tNTPjqwHvA+0Lzu9e0txk8DjfqH1p9UJRw==}
+ engines: {node: '>=20'}
+
+ '@shikijs/types@4.6.0':
+ resolution: {integrity: sha512-+ENG1adsKt/X7cRYw1Nl7BEpBmPOjmQ7bR/eGmHuMlT8gtOFf3KEi5l31DJYyAWhW6Y6bjP1plU7FmaxWGENFw==}
+ engines: {node: '>=20'}
+
+ '@shikijs/vscode-textmate@10.1.0':
+ resolution: {integrity: sha512-cVBuhDQjBZaNMTwBquYfPydrUP+E3hE6oYGasx8h6ZrODIM7/r+gxLDXdd66oszbrggZHEntdE6j+5gYIVyMWQ==}
+
+ '@shuding/opentype.js@1.4.0-beta.0':
+ resolution: {integrity: sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA==}
+ engines: {node: '>= 8.0.0'}
+ hasBin: true
+
+ '@tailwindcss/node@4.3.3':
+ resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==}
+
+ '@tailwindcss/oxide-android-arm64@4.3.3':
+ resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [android]
+
+ '@tailwindcss/oxide-darwin-arm64@4.3.3':
+ resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-darwin-x64@4.3.3':
+ resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-freebsd-x64@4.3.3':
+ resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3':
+ resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==}
+ engines: {node: '>= 20'}
+ cpu: [arm]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.3.3':
+ resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.3.3':
+ resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.3.3':
+ resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@tailwindcss/oxide-linux-x64-musl@4.3.3':
+ resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@tailwindcss/oxide-wasm32-wasi@4.3.3':
+ resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+ bundledDependencies:
+ - '@napi-rs/wasm-runtime'
+ - '@emnapi/core'
+ - '@emnapi/runtime'
+ - '@tybys/wasm-util'
+ - '@emnapi/wasi-threads'
+ - tslib
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.3.3':
+ resolution: {integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.3.3':
+ resolution: {integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/oxide@4.3.3':
+ resolution: {integrity: sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==}
+ engines: {node: '>= 20'}
+
+ '@tailwindcss/typography@0.5.20':
+ resolution: {integrity: sha512-hwbzQuNUfcPvbegQFatVPl/MY/tcM9KLl963hQ5laJKPh81TEZ1+dNG9PirGvcaDBkp+BCshExAyKVPW91dozw==}
+ peerDependencies:
+ tailwindcss: '>=3.0.0 || >=4.0.0 || insiders'
+
+ '@tailwindcss/vite@4.3.3':
+ resolution: {integrity: sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==}
+ peerDependencies:
+ vite: ^5.2.0 || ^6 || ^7 || ^8
+
+ '@tybys/wasm-util@0.10.4':
+ resolution: {integrity: sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==}
+
+ '@types/debug@4.1.13':
+ resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==}
+
+ '@types/esrecurse@4.3.1':
+ resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==}
+
+ '@types/estree-jsx@1.0.5':
+ resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
+
+ '@types/estree@1.0.9':
+ resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
+
+ '@types/hast@3.0.5':
+ resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==}
+
+ '@types/json-schema@7.0.15':
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+ '@types/lodash.kebabcase@4.1.9':
+ resolution: {integrity: sha512-kPrrmcVOhSsjAVRovN0lRfrbuidfg0wYsrQa5IYuoQO1fpHHGSme66oyiYA/5eQPVl8Z95OA3HG0+d2SvYC85w==}
+
+ '@types/lodash@4.17.27':
+ resolution: {integrity: sha512-VenMRv/eKNrspX9v3H4dbmjsrnCOYdYBG3kxXKk3dG6X+urBGcj2Prww3xw1U5K4FV/mE4h5GbWd0szEKf+9ew==}
+
+ '@types/mdast@4.0.4':
+ resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
+
+ '@types/mdx@2.0.14':
+ resolution: {integrity: sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg==}
+
+ '@types/ms@2.1.0':
+ resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
+
+ '@types/nlcst@2.0.3':
+ resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==}
+
+ '@types/node@24.19.2':
+ resolution: {integrity: sha512-93d49wCWJkCyN87MxLpNN6KXPCwFw7bVUg79mGUV1OS40JGWwVCr8n61DXCSqz7CV6/VfPkb7xec7uP2M+QHWQ==}
+
+ '@types/sax@1.2.7':
+ resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==}
+
+ '@types/ungap__structured-clone@1.2.0':
+ resolution: {integrity: sha512-ZoaihZNLeZSxESbk9PUAPZOlSpcKx81I1+4emtULDVmBLkYutTcMlCj2K9VNlf9EWODxdO6gkAqEaLorXwZQVA==}
+
+ '@types/unist@2.0.11':
+ resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
+
+ '@types/unist@3.0.3':
+ resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
+
+ '@typescript-eslint/parser@8.72.1':
+ resolution: {integrity: sha512-ZGwZP4OCfyA0AQTpEAMWz+rtX01DRhQWW5hB0ucKDEIX5U7jdmPSaMx94rQfaPAQzIYz2BimOgKOJtjfpRFQ9g==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/project-service@8.72.1':
+ resolution: {integrity: sha512-fAuVMkwdO/huI8nyo/WKnNKf6Gag93S56Rh3Ts5mGcavwSdaN8tRg1gCeIGX8PF+nzp1BjGfO4gHhGzW1J6jTg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/scope-manager@8.72.1':
+ resolution: {integrity: sha512-lC/t6eMCZZQAAKZEYGL6RDVSuFehpdmMT6h+nJIjIa6oylN/PwDgDVtEIbB7O9xFfZYtZAYLoLlDNWKJhtF+uQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/tsconfig-utils@8.72.1':
+ resolution: {integrity: sha512-e4D5R5vpeAEPBxT2BAzsFQL3sbwyeeeo5+aczgNSRK1V734jE7pTqaGFU+fXapWEQwnZbpFaTn32+Z8DwS6OTg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/types@8.72.1':
+ resolution: {integrity: sha512-LmE5IhQet1BWOWEXqBrVtBPsMAOQ6Nzk35d+WLh3mk4EUF33sqlfxjio1MdYeSvKSG5skactKKuixBIKXhL4Rw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/typescript-estree@8.72.1':
+ resolution: {integrity: sha512-DAlUoUV93hIvrtIQLwCbmZQYVZwOMhiHJrOVUUIWicFZaIYjTRqlY3I/NZevfkLmSqkNHRu2qYDcq9lrn16oOQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/visitor-keys@8.72.1':
+ resolution: {integrity: sha512-SB8xqipSzZHUePCCLNeYxqnxq83X61/qzNhjQGbTfjxGZIeOEP7tBgWYX05Tcd6CgE02WdTJnh5b8kMZJ/+bnQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@ungap/structured-clone@1.4.0':
+ resolution: {integrity: sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==}
+
+ '@volar/kit@2.4.30':
+ resolution: {integrity: sha512-VocLtumfXnqnPQEWc9Yx7uC1uOh9gyjVNlrn2+6Zx/z9aM6yTLhPK8lSqM9Ilu81XRa48I00lcYJWLi2BLxx8Q==}
+ peerDependencies:
+ typescript: <7
+
+ '@volar/language-core@2.4.30':
+ resolution: {integrity: sha512-73fjwEV8wat6aOZ6U/y/hqtE30ocrIWECg67b0txGczOMNw+MffLmvJ5fnFfN9drVUEYZzLdaNo6VAy6G6avMw==}
+
+ '@volar/language-server@2.4.30':
+ resolution: {integrity: sha512-tcP9fzzUBbsLIFHhgnRU4ncQjMn9G9Vax0ffkQmdCrXVEYqRroUepmyC5AVgiju5bCiLD6a55qen5iOTQHkb3g==}
+ peerDependencies:
+ typescript: <7
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@volar/language-service@2.4.30':
+ resolution: {integrity: sha512-w/xDsASiqC71ggCOm7sTDXDNk/l72XEptodeRC68tgZ0e5QM4hM3zAqBGC7mBctvhrpYwKfMG+t9xcUo2hEFtA==}
+ peerDependencies:
+ typescript: <7
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@volar/source-map@2.4.30':
+ resolution: {integrity: sha512-zym1ob/KwDDyN+0jGVtxwbxJSRRnUvv5HspCtz3yEfVVdCG4/dzxJktMGPAgemqRuVs81O0o/y0q3qOUErR8kg==}
+
+ '@volar/typescript@2.4.30':
+ resolution: {integrity: sha512-/DgA5tgYFPfrLFuSjIMXMGHk1LQv2+P8eny6+BhGNzFJELWIW7UZlc56ZLIRFhLsPpMfIFxM8/HvOPBggrjTZg==}
+ peerDependencies:
+ typescript: <7
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@vscode/emmet-helper@2.11.0':
+ resolution: {integrity: sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw==}
+
+ '@vscode/l10n@0.0.18':
+ resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==}
+
+ acorn-jsx@5.3.2:
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ acorn@8.19.0:
+ resolution: {integrity: sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ ajv-draft-04@1.0.0:
+ resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==}
+ peerDependencies:
+ ajv: ^8.5.0
+ peerDependenciesMeta:
+ ajv:
+ optional: true
+
+ ajv-i18n@4.2.0:
+ resolution: {integrity: sha512-v/ei2UkCEeuKNXh8RToiFsUclmU+G57LO1Oo22OagNMENIw+Yb8eMwvHu7Vn9fmkjJyv6XclhJ8TbuigSglPkg==}
+ peerDependencies:
+ ajv: ^8.0.0-beta.0
+
+ ajv@6.15.0:
+ resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==}
+
+ ajv@8.20.0:
+ resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==}
+
+ am-i-vibing@0.5.0:
+ resolution: {integrity: sha512-KLcsDhqf1XRrJmJr+9I/C0EX2K+sGrAGZFgkmd84c8TJO9zJBu+/kFInjazu1KfJWFhWDKdRWkpBzEs7s35DVA==}
+ hasBin: true
+
+ ansi-regex@6.4.0:
+ resolution: {integrity: sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==}
+ engines: {node: '>=12'}
+
+ ansi-styles@6.2.3:
+ resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
+ engines: {node: '>=12'}
+
+ anymatch@3.1.3:
+ resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+ engines: {node: '>= 8'}
+
+ anynum@1.0.1:
+ resolution: {integrity: sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==}
+
+ arg@5.0.2:
+ resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ aria-query@5.3.2:
+ resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
+ engines: {node: '>= 0.4'}
+
+ array-iterate@2.0.1:
+ resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==}
+
+ astring@1.9.0:
+ resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==}
+ hasBin: true
+
+ astro-eslint-parser@3.2.0:
+ resolution: {integrity: sha512-WavQUarfgwSbpCq4xsdcIBdX5KArfI2d6DD8EIloJIMthnB1508iNpx4C77EhUyMd03LpEt8HAPzwF/cG0V39g==}
+ engines: {node: ^22.22.3 || ^24.16.0 || >=26.3.0}
+
+ astro@7.4.5:
+ resolution: {integrity: sha512-n5lET11/qnrOWn4wNpXxD9g2VKkTIa162hHgrkMPChMQC5q/xh2lY2OerK5mpsEAFCNmXsYA7NISE+9Uo7Cxxg==}
+ engines: {node: '>=22.23.4', npm: '>=9.6.5', pnpm: '>=7.1.0'}
+ hasBin: true
+ peerDependencies:
+ '@astrojs/markdown-remark': ^7.3.0
+ peerDependenciesMeta:
+ '@astrojs/markdown-remark':
+ optional: true
+
+ axobject-query@4.1.0:
+ resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
+ engines: {node: '>= 0.4'}
+
+ bail@2.0.2:
+ resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==}
+
+ balanced-match@4.0.4:
+ resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
+ engines: {node: 18 || 20 || >=22}
+
+ base64-js@0.0.8:
+ resolution: {integrity: sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==}
+ engines: {node: '>= 0.4'}
+
+ boolbase@1.0.0:
+ resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+
+ brace-expansion@5.0.12:
+ resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==}
+ engines: {node: 20 || >=22}
+
+ cacheable@2.5.0:
+ resolution: {integrity: sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==}
+
+ camelize@1.0.1:
+ resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}
+
+ ccount@2.0.1:
+ resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+
+ character-entities-html4@2.1.0:
+ resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==}
+
+ character-entities-legacy@3.0.0:
+ resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==}
+
+ character-entities@2.0.2:
+ resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
+
+ character-reference-invalid@2.0.1:
+ resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==}
+
+ chokidar@5.0.0:
+ resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==}
+ engines: {node: '>= 20.19.0'}
+
+ ci-info@4.4.0:
+ resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==}
+ engines: {node: '>=8'}
+
+ cliui@9.0.1:
+ resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==}
+ engines: {node: '>=20'}
+
+ clsx@2.1.1:
+ resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
+ engines: {node: '>=6'}
+
+ collapse-white-space@2.1.0:
+ resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ comma-separated-tokens@2.0.3:
+ resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
+
+ commander@11.1.0:
+ resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==}
+ engines: {node: '>=16'}
+
+ common-ancestor-path@2.0.0:
+ resolution: {integrity: sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==}
+ engines: {node: '>= 18'}
+
+ cookie-es@1.2.3:
+ resolution: {integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==}
+
+ cookie@2.0.1:
+ resolution: {integrity: sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w==}
+ engines: {node: '>=22'}
+
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
+ crossws@0.3.5:
+ resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==}
+
+ css-background-parser@0.1.0:
+ resolution: {integrity: sha512-2EZLisiZQ+7m4wwur/qiYJRniHX4K5Tc9w93MT3AS0WS1u5kaZ4FKXlOTBhOjc+CgEgPiGY+fX1yWD8UwpEqUA==}
+
+ css-box-shadow@1.0.0-3:
+ resolution: {integrity: sha512-9jaqR6e7Ohds+aWwmhe6wILJ99xYQbfmK9QQB9CcMjDbTxPZjwEmUQpU91OG05Xgm8BahT5fW+svbsQGjS/zPg==}
+
+ css-color-keywords@1.0.0:
+ resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==}
+ engines: {node: '>=4'}
+
+ css-gradient-parser@0.0.17:
+ resolution: {integrity: sha512-w2Xy9UMMwlKtou0vlRnXvWglPAceXCTtcmVSo8ZBUvqCV5aXEFP/PC6d+I464810I9FT++UACwTD5511bmGPUg==}
+ engines: {node: '>=16'}
+
+ css-select@6.0.0:
+ resolution: {integrity: sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==}
+
+ css-to-react-native@3.2.0:
+ resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==}
+
+ css-tree@2.2.1:
+ resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
+
+ css-tree@3.2.1:
+ resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+
+ css-what@7.0.0:
+ resolution: {integrity: sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==}
+ engines: {node: '>= 6'}
+
+ cssesc@3.0.0:
+ resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ csso@5.0.5:
+ resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
+
+ dayjs@1.11.23:
+ resolution: {integrity: sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==}
+
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ decode-named-character-reference@1.3.0:
+ resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==}
+
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+ defu@6.1.7:
+ resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==}
+
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
+ destr@2.0.5:
+ resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==}
+
+ detect-libc@2.1.2:
+ resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
+ engines: {node: '>=8'}
+
+ devalue@6.0.3:
+ resolution: {integrity: sha512-3woMEITftG1wKeO/4d/QXH1DVKctTSZg3jNGPpjp9hNr5Hos8MCFF5McTML5ZtgKDObm2R591B0spRp2IVernA==}
+ engines: {node: '>=18'}
+
+ devlop@1.1.0:
+ resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+
+ diff@9.0.0:
+ resolution: {integrity: sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==}
+ engines: {node: '>=0.3.1'}
+
+ dom-serializer@2.0.0:
+ resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
+
+ domelementtype@2.3.0:
+ resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
+
+ domhandler@5.0.3:
+ resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
+ engines: {node: '>= 4'}
+
+ domutils@3.2.2:
+ resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
+
+ dset@3.1.4:
+ resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==}
+ engines: {node: '>=4'}
+
+ emmet@2.4.11:
+ resolution: {integrity: sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==}
+
+ emoji-regex-xs@2.0.1:
+ resolution: {integrity: sha512-1QFuh8l7LqUcKe24LsPUNzjrzJQ7pgRwp1QMcZ5MX6mFplk2zQ08NVCM84++1cveaUUYtcCYHmeFEuNg16sU4g==}
+ engines: {node: '>=10.0.0'}
+
+ emoji-regex@10.6.0:
+ resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
+
+ enhanced-resolve@5.26.0:
+ resolution: {integrity: sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==}
+ engines: {node: '>=10.13.0'}
+
+ entities@4.5.0:
+ resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+ engines: {node: '>=0.12'}
+
+ entities@6.0.1:
+ resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
+ engines: {node: '>=0.12'}
+
+ es-module-lexer@2.3.2:
+ resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==}
+
+ es-module-lexer@3.0.4:
+ resolution: {integrity: sha512-BldNY5mwkVu+cQ5Ymn3e6hB9Sx8XHx4ZevWbiUyZbfKxJxYO27Hf0wfzkxIaD5CAJMaGl3YspKELj0ff/7nCMA==}
+
+ esast-util-from-estree@2.0.0:
+ resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==}
+
+ esast-util-from-js@2.0.1:
+ resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==}
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ escape-html@1.0.3:
+ resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
+ escape-string-regexp@5.0.0:
+ resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
+ engines: {node: '>=12'}
+
+ eslint-plugin-astro@3.2.1:
+ resolution: {integrity: sha512-IgU7LIkpEg0r6bQGwc3Ck5jHgkUk3rOrEfRhUxDtMx/M0GD3SG/turMtvwj989KRR12Wt1l89faZ5ynjvVL7hg==}
+ engines: {node: ^22.22.3 || ^24.16.0 || >=26.3.0}
+ peerDependencies:
+ '@typescript-eslint/parser': '>=8.61.0'
+ eslint: '>=10.0.0'
+ eslint-plugin-jsx-a11y: '>=6.10.2'
+ eslint-plugin-jsx-a11y-x: '>=0.2.0'
+ typescript-eslint: '>=8.61.0'
+ peerDependenciesMeta:
+ '@typescript-eslint/parser':
+ optional: true
+ eslint-plugin-jsx-a11y:
+ optional: true
+ eslint-plugin-jsx-a11y-x:
+ optional: true
+ typescript-eslint:
+ optional: true
+
+ eslint-scope@9.1.2:
+ resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ eslint-visitor-keys@3.4.3:
+ resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint-visitor-keys@5.0.1:
+ resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ eslint@10.14.0:
+ resolution: {integrity: sha512-T0pNsxkb9IKmtQjhpK3d7nWOpFv+YHuMbTJnSVMoQblF0onvXfErrtp4V1jHtsQPan7dKaGVD0q1WCXnzMF31A==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+ hasBin: true
+ peerDependencies:
+ jiti: '*'
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+
+ espree@11.2.0:
+ resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ esquery@1.7.0:
+ resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==}
+ engines: {node: '>=0.10'}
+
+ esrecurse@4.3.0:
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+ engines: {node: '>=4.0'}
+
+ estraverse@5.3.0:
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+ engines: {node: '>=4.0'}
+
+ estree-util-attach-comments@3.0.0:
+ resolution: {integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==}
+
+ estree-util-build-jsx@3.0.1:
+ resolution: {integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==}
+
+ estree-util-is-identifier-name@3.0.0:
+ resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==}
+
+ estree-util-scope@1.0.1:
+ resolution: {integrity: sha512-B0np3dcdxqILX5e9nEi5/Fr4K7gL4oYFVPV1zRa2e9wRCbQoZZNWOZFYyoInvXUPJXBXjss+QXlWLJChDEHDkA==}
+
+ estree-util-to-js@2.0.0:
+ resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==}
+
+ estree-util-visit@2.0.0:
+ resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==}
+
+ estree-walker@3.0.3:
+ resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ eventemitter3@5.0.4:
+ resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==}
+
+ extend@3.0.2:
+ resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-levenshtein@2.0.6:
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+ fast-string-truncated-width@3.0.3:
+ resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==}
+
+ fast-string-width@3.0.2:
+ resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==}
+
+ fast-uri@3.1.9:
+ resolution: {integrity: sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==}
+
+ fast-wrap-ansi@0.2.2:
+ resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==}
+
+ fast-xml-builder@1.3.1:
+ resolution: {integrity: sha512-pIM/1n3ntFXKYrUZwW7QCK0gAW7XY+wzj1YMIV3tLDvPj/V+zTGJK5e3/4WJfwj0qWw2ElNXiTixda/R+3YSug==}
+
+ fast-xml-parser@5.11.2:
+ resolution: {integrity: sha512-R9iDuNrQYeQut46cn2r2wHKn4HYzVDvDm5J1wW+koZewykv0yuO2HChTYeZtrULyHIDM9cj9TUXloCsueCQUog==}
+ hasBin: true
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ fflate@0.7.3:
+ resolution: {integrity: sha512-0Zz1jOzJWERhyhsimS54VTqOteCNwRtIlh8isdL0AXLo0g7xNTfTL7oWrkmCnPhZGocKIkWHBistBrrpoNH3aw==}
+
+ fflate@0.7.5:
+ resolution: {integrity: sha512-QieYf//cis6ywHNi5qW1+PXPQ4bC+XVJAtS4AXIML8P76GroEiOxm/oQtn1f02UkJY1+KsXMJcC+R2v/Eg4G3g==}
+
+ file-entry-cache@11.1.5:
+ resolution: {integrity: sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==}
+
+ find-up@5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+
+ flat-cache@6.1.23:
+ resolution: {integrity: sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==}
+
+ flatted@3.4.4:
+ resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==}
+
+ flattie@1.1.1:
+ resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==}
+ engines: {node: '>=8'}
+
+ fnv1a-64@0.1.2:
+ resolution: {integrity: sha512-mJbcILTPybAR1jdOwt/lVv8N2cffeJFFP+gVbSAwLEx1ujXH27RpPd8Rokec/KX9c76UYIB6Co+H4lQzxPnJfA==}
+
+ fontace@0.4.1:
+ resolution: {integrity: sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==}
+
+ fontkitten@1.0.3:
+ resolution: {integrity: sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==}
+ engines: {node: '>=20'}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ get-caller-file@2.0.5:
+ resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+ engines: {node: 6.* || 8.* || >= 10.*}
+
+ get-east-asian-width@1.7.0:
+ resolution: {integrity: sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==}
+ engines: {node: '>=18'}
+
+ get-tsconfig@5.0.0-beta.6:
+ resolution: {integrity: sha512-X6fBC0pmImC70gvX2zm56go9hx0MyoGVdG0tUCkg/D+Xnh5TJsOZ7iDbOdI3PvmtrDxnu1YdDufpK2QJX1Meqw==}
+ engines: {node: '>=20.20.0'}
+
+ github-slugger@2.0.0:
+ resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==}
+
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
+ globals@17.13.0:
+ resolution: {integrity: sha512-RwMTC61u7hrG3ZJMkZQOK4JLJrKS8QtoTii63EmWvFXHqycY9FObBJQCbsLxSO8kjKhWE5kV1GC+Vb1g3RI0Zg==}
+ engines: {node: '>=18'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ h3@1.15.11:
+ resolution: {integrity: sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==}
+
+ harfbuzzjs@0.10.0:
+ resolution: {integrity: sha512-SN8LVwCOzvTq3OPNd0+EAghgXugItz56wst567D1vs7LnnKGWprhi3EG58aVOBwhN8HEbQxL4I9NDWkcXUutRw==}
+
+ hashery@1.5.1:
+ resolution: {integrity: sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==}
+ engines: {node: '>=20'}
+
+ hast-util-from-html@2.0.3:
+ resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==}
+
+ hast-util-from-parse5@8.0.3:
+ resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==}
+
+ hast-util-is-element@3.0.0:
+ resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==}
+
+ hast-util-parse-selector@4.0.0:
+ resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==}
+
+ hast-util-raw@9.1.0:
+ resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==}
+
+ hast-util-to-estree@3.1.3:
+ resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==}
+
+ hast-util-to-html@9.0.5:
+ resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==}
+
+ hast-util-to-html@9.0.6:
+ resolution: {integrity: sha512-1SZUxCN3TtJAE7e2BQN2MBHsoZ1gnmi9GoCP5Di59CSWptIezSAW09yDZyVbanb78V9eCwrKqdQsjtfVTxYHWA==}
+
+ hast-util-to-jsx-runtime@2.3.6:
+ resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==}
+
+ hast-util-to-parse5@8.0.1:
+ resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==}
+
+ hast-util-to-text@4.0.3:
+ resolution: {integrity: sha512-5yrlWOvGFs6CqHriUpSsNrjzT/Tdo+IOQudbznxOn8K5qblp18MJyzOg8IgDOKQ8MYgQcJA7SusuCjSpDr8mSA==}
+
+ hast-util-whitespace@3.0.0:
+ resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
+
+ hastscript@9.0.1:
+ resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==}
+
+ hex-rgb@4.3.0:
+ resolution: {integrity: sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==}
+ engines: {node: '>=6'}
+
+ hookified@1.15.1:
+ resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==}
+
+ hookified@2.2.0:
+ resolution: {integrity: sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==}
+
+ html-escaper@3.0.3:
+ resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==}
+
+ html-void-elements@3.0.0:
+ resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
+
+ http-cache-semantics@4.3.0:
+ resolution: {integrity: sha512-M5t5LlJpS1UHMjvwRQVdFHvPISGeLAxNcrWuJkeGh0KxsqCHZ1O3NXZU/8x7cD0BDcGW8kapxMKTvwlqrNkHkA==}
+
+ ignore@5.3.2:
+ resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+ engines: {node: '>= 4'}
+
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
+ inline-style-parser@0.2.7:
+ resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==}
+
+ iron-webcrypto@1.2.1:
+ resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==}
+
+ is-alphabetical@2.0.1:
+ resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==}
+
+ is-alphanumerical@2.0.1:
+ resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==}
+
+ is-decimal@2.0.1:
+ resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==}
+
+ is-docker@4.0.0:
+ resolution: {integrity: sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==}
+ engines: {node: '>=20'}
+ hasBin: true
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-hexadecimal@2.0.1:
+ resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==}
+
+ is-plain-obj@4.1.0:
+ resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
+ engines: {node: '>=12'}
+
+ is-unsafe@2.0.2:
+ resolution: {integrity: sha512-HgbIHPBH0KHHCcjLfGsCvhtPTVxjaAZlXjwdz7/GQC40SjSe4sfQsar8J5VFo8JOSbarkpV0OLG95bbaNd9aAQ==}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ jiti@2.7.0:
+ resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
+ hasBin: true
+
+ js-yaml@4.3.2:
+ resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==}
+ hasBin: true
+
+ json-schema-traverse@0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+ json-schema-traverse@1.0.0:
+ resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
+
+ json-stable-stringify-without-jsonify@1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+ jsonc-parser@2.3.1:
+ resolution: {integrity: sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==}
+
+ jsonc-parser@3.3.1:
+ resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==}
+
+ keyv@5.6.0:
+ resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==}
+
+ kleur@4.1.5:
+ resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
+ engines: {node: '>=6'}
+
+ levn@0.4.1:
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+ engines: {node: '>= 0.8.0'}
+
+ lightningcss-android-arm64@1.32.0:
+ resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ lightningcss-android-arm64@1.33.0:
+ resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ lightningcss-darwin-arm64@1.32.0:
+ resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-arm64@1.33.0:
+ resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.32.0:
+ resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.33.0:
+ resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.32.0:
+ resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-freebsd-x64@1.33.0:
+ resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.32.0:
+ resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm-gnueabihf@1.33.0:
+ resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.32.0:
+ resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-arm64-gnu@1.33.0:
+ resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-arm64-musl@1.32.0:
+ resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-linux-arm64-musl@1.33.0:
+ resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-linux-x64-gnu@1.32.0:
+ resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-x64-gnu@1.33.0:
+ resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-x64-musl@1.32.0:
+ resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-linux-x64-musl@1.33.0:
+ resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-win32-arm64-msvc@1.32.0:
+ resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-arm64-msvc@1.33.0:
+ resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.32.0:
+ resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.33.0:
+ resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.32.0:
+ resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
+ engines: {node: '>= 12.0.0'}
+
+ lightningcss@1.33.0:
+ resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==}
+ engines: {node: '>= 12.0.0'}
+
+ linebreak@1.1.0:
+ resolution: {integrity: sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==}
+
+ locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+
+ lodash.kebabcase@4.1.1:
+ resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==}
+
+ longest-streak@3.1.0:
+ resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
+
+ lru-cache@11.5.3:
+ resolution: {integrity: sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==}
+ engines: {node: 20 || >=22}
+
+ magic-string@0.30.21:
+ resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+
+ magic-string@1.4.5:
+ resolution: {integrity: sha512-oLLC2+YvQUfAa+vxoufE/yc1icb87M9iXIRqAoR/2EhE/8cb3SF/+Gi0c2By1B+5Vz2e2vQtUvHiu1+63Znh+w==}
+
+ magicast@0.5.5:
+ resolution: {integrity: sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==}
+
+ markdown-extensions@2.0.0:
+ resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==}
+ engines: {node: '>=16'}
+
+ markdown-table@3.0.4:
+ resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
+
+ mdast-util-definitions@6.0.0:
+ resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==}
+
+ mdast-util-find-and-replace@3.0.4:
+ resolution: {integrity: sha512-z4B5c6jrnJ1ltOQemhWYPBSAo6y42Z8Gt2yEgfgNTMcLSU59B8bfj8Z8AnAlJrBGLnqkdAUZ7jdDEqQw3KuipQ==}
+
+ mdast-util-from-markdown@2.1.0:
+ resolution: {integrity: sha512-KRLgmPaP2H1ul2bpoJkOS+9pAcDAAj9Nu1mqLnh8NLeJG5tlvj42TH80PIGBpROWlRXLk0lDrWNuZCFy/tsB4A==}
+
+ mdast-util-gfm-autolink-literal@2.0.1:
+ resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==}
+
+ mdast-util-gfm-footnote@2.1.0:
+ resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==}
+
+ mdast-util-gfm-strikethrough@2.0.1:
+ resolution: {integrity: sha512-OuJHqvr455pwu2OaOrir7dbzqs4jlWKOtlu9L6GjcIjjQwNyw02VntQsr/Ek6/A13vgnXfUhBekWRUP1OkNivw==}
+
+ mdast-util-gfm-table@2.0.0:
+ resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==}
+
+ mdast-util-gfm-task-list-item@2.0.0:
+ resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==}
+
+ mdast-util-gfm@3.1.0:
+ resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==}
+
+ mdast-util-heading-range@2.1.5:
+ resolution: {integrity: sha512-jXbFD0C+MfRkwsaze+btzG9CmVrxnc5kpcJLtx3SvSlPWnNdGMlDRHKDB9/TIPEq9nRHnkixppT8yvaUJ5agJg==}
+
+ mdast-util-mdx-expression@2.0.1:
+ resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==}
+
+ mdast-util-mdx-jsx@3.2.0:
+ resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==}
+
+ mdast-util-mdx@3.0.0:
+ resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==}
+
+ mdast-util-mdxjs-esm@2.0.1:
+ resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==}
+
+ mdast-util-phrasing@4.1.0:
+ resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==}
+
+ mdast-util-to-hast@13.2.1:
+ resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==}
+
+ mdast-util-to-markdown@2.2.1:
+ resolution: {integrity: sha512-ThatlCBxG2GE+mljhNSfZPv56d3QrGiRh97jjYgIk80IzS4uU6d7cHAaexCYSAmUCCcOs3grJ07kX3oTGsFUOw==}
+
+ mdast-util-to-string@1.1.0:
+ resolution: {integrity: sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A==}
+
+ mdast-util-to-string@4.0.0:
+ resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
+
+ mdast-util-toc@7.1.0:
+ resolution: {integrity: sha512-2TVKotOQzqdY7THOdn2gGzS9d1Sdd66bvxUyw3aNpWfcPXCLYSJCCgfPy30sEtuzkDraJgqF35dzgmz6xlvH/w==}
+
+ mdn-data@2.0.28:
+ resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
+
+ mdn-data@2.27.1:
+ resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==}
+
+ micromark-core-commonmark@2.0.4:
+ resolution: {integrity: sha512-wxEeE8v8XVvOrxn1TZj74qYhAtTQsSqufHVS3uNVyT1MupXxhyaHk8/9xDNPh9BmL77QNDgWTkZ3RsYHB2ry7w==}
+
+ micromark-extension-gfm-autolink-literal@2.1.0:
+ resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==}
+
+ micromark-extension-gfm-footnote@2.1.0:
+ resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==}
+
+ micromark-extension-gfm-strikethrough@2.1.0:
+ resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==}
+
+ micromark-extension-gfm-table@2.1.2:
+ resolution: {integrity: sha512-pRzm4kDTu0MjlmBkxmS9yYhw60nncfcEwu9NNdPFSQEFXS95ZKyIIyTSHu/o3ReBUrLKYEq+7YaXCRn/bPB4MA==}
+
+ micromark-extension-gfm-tagfilter@2.0.0:
+ resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==}
+
+ micromark-extension-gfm-task-list-item@2.1.0:
+ resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==}
+
+ micromark-extension-gfm@3.0.0:
+ resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==}
+
+ micromark-extension-mdx-expression@3.0.1:
+ resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==}
+
+ micromark-extension-mdx-jsx@3.0.2:
+ resolution: {integrity: sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==}
+
+ micromark-extension-mdx-md@2.0.0:
+ resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==}
+
+ micromark-extension-mdxjs-esm@3.0.0:
+ resolution: {integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==}
+
+ micromark-extension-mdxjs@3.0.0:
+ resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==}
+
+ micromark-factory-destination@2.0.1:
+ resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==}
+
+ micromark-factory-label@2.0.1:
+ resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==}
+
+ micromark-factory-mdx-expression@2.0.3:
+ resolution: {integrity: sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==}
+
+ micromark-factory-space@2.1.0:
+ resolution: {integrity: sha512-fS8hnLIjnjvdQIj39Geug8wWsR0HrYZ43KShKxNfwT7t2LOHo/LbWZEzEaSOjwtjdCsjoE6syHhonEzW0zv0+Q==}
+
+ micromark-factory-title@2.0.1:
+ resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==}
+
+ micromark-factory-whitespace@2.0.1:
+ resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==}
+
+ micromark-util-character@2.1.1:
+ resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==}
+
+ micromark-util-chunked@2.0.1:
+ resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==}
+
+ micromark-util-classify-character@2.0.1:
+ resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==}
+
+ micromark-util-combine-extensions@2.0.1:
+ resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==}
+
+ micromark-util-decode-numeric-character-reference@2.0.2:
+ resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==}
+
+ micromark-util-decode-string@2.0.1:
+ resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==}
+
+ micromark-util-edit-map@1.0.0:
+ resolution: {integrity: sha512-Pa2ljlsEL6sVwFaYeyrOLSYbQt73JvGbPOYFq+9AElXiSnfI5Q4465RRZ1sHv7lDjDOnGRDDaqPXqClqWK/q1Q==}
+
+ micromark-util-encode@2.0.1:
+ resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==}
+
+ micromark-util-events-to-acorn@2.0.3:
+ resolution: {integrity: sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==}
+
+ micromark-util-html-tag-name@2.0.1:
+ resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==}
+
+ micromark-util-normalize-identifier@2.0.1:
+ resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==}
+
+ micromark-util-resolve-all@2.0.1:
+ resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==}
+
+ micromark-util-sanitize-uri@2.0.1:
+ resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==}
+
+ micromark-util-subtokenize@2.1.0:
+ resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==}
+
+ micromark-util-symbol@2.0.1:
+ resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==}
+
+ micromark-util-types@2.0.3:
+ resolution: {integrity: sha512-oxB2Ik03hI0gv+VNn9tnh1t1YEe9MDPptViAEgfdf3YQHsn0pzGTgCdlSCJXcwhqm8phaEuM7zeEu3QQzVBrPg==}
+
+ micromark@4.0.3:
+ resolution: {integrity: sha512-oGYfQzHSG5dOMovQcJ3fyTmZlWAWpi0XA0sJwJs+i6OT88o1+Jtw/8z0CmdowSLxGhhFK89rQ/oXph/wN02PNw==}
+
+ minimatch@10.2.6:
+ resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==}
+ engines: {node: 18 || 20 || >=22}
+
+ mrmime@2.0.1:
+ resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
+ engines: {node: '>=10'}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ muggle-string@0.4.1:
+ resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
+
+ nanoid@3.3.20:
+ resolution: {integrity: sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+ neotraverse@1.0.1:
+ resolution: {integrity: sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w==}
+ engines: {node: '>= 10'}
+
+ nlcst-to-string@4.0.0:
+ resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==}
+
+ node-fetch-native@1.6.7:
+ resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
+
+ node-mock-http@1.0.5:
+ resolution: {integrity: sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==}
+
+ normalize-path@3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+
+ nth-check@2.1.1:
+ resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+
+ obug@3.0.0:
+ resolution: {integrity: sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==}
+ engines: {node: '>=12.20.0'}
+
+ ofetch@1.5.1:
+ resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==}
+
+ oniguruma-parser@0.12.2:
+ resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==}
+
+ oniguruma-to-es@4.3.6:
+ resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==}
+
+ optionator@0.9.4:
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+ engines: {node: '>= 0.8.0'}
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-limit@7.3.3:
+ resolution: {integrity: sha512-SKgVvDU5TNIxQ4r30U75BNLC6a+6GOEjngrk/ysM5+Zu3oJuk1SC5ApzIzY/7PjCEjoVPMWJ0zho/4QUiEB7TQ==}
+ engines: {node: '>=20'}
+
+ p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+
+ p-queue@9.3.5:
+ resolution: {integrity: sha512-Lki/zhXFVmDhesBUn/yHRLW9Bte7mE7xoFHzTR8sIomV7a2z+LxYAPkGvdfidNiu4fKSfjtawixxSHcZci5QcQ==}
+ engines: {node: '>=20'}
+
+ p-timeout@7.0.2:
+ resolution: {integrity: sha512-prbX4Z3YszrFNgH+MW5Zoeq3baXrMtP/MQnFeET90UB/GtGcGDQ5Usg9OCy6ETjTTntOw1SL2z9fMPUppN3Guw==}
+ engines: {node: '>=20'}
+
+ package-manager-detector@1.9.0:
+ resolution: {integrity: sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA==}
+
+ pagefind@1.6.0:
+ resolution: {integrity: sha512-yeOd9EA0TUE1PUfbQIluBxzPadBG5/0BV48FAxwMVLkhZcOvwCz6TX4/a/Ofz97pF9/FwwT7CVbTC97BfmI+LQ==}
+ hasBin: true
+
+ pako@0.2.9:
+ resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==}
+
+ parse-css-color@0.2.1:
+ resolution: {integrity: sha512-bwS/GGIFV3b6KS4uwpzCFj4w297Yl3uqnSgIPsoQkx7GMLROXfMnWvxfNkL0oh8HVhZA4hvJoEoEIqonfJ3BWg==}
+
+ parse-entities@4.0.2:
+ resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==}
+
+ parse-latin@7.0.0:
+ resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==}
+
+ parse5@7.3.0:
+ resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
+
+ path-browserify@1.0.1:
+ resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
+ path-expression-matcher@1.6.2:
+ resolution: {integrity: sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==}
+ engines: {node: '>=14.0.0'}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ piccolore@0.1.3:
+ resolution: {integrity: sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@2.3.2:
+ resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==}
+ engines: {node: '>=8.6'}
+
+ picomatch@4.0.7:
+ resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==}
+ engines: {node: '>=12'}
+
+ postcss-selector-parser@6.0.10:
+ resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
+ engines: {node: '>=4'}
+
+ postcss-selector-parser@7.1.7:
+ resolution: {integrity: sha512-wWBro/7hJ40ye6TPiXkUQoXKCw23xSsjriUvqd7Uls9Sso3oLiON1flc18cCdnjgCRXw4fBVqUJJeOdDTh3P3g==}
+ engines: {node: '>=4'}
+
+ postcss-value-parser@4.2.0:
+ resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+
+ postcss@8.5.32:
+ resolution: {integrity: sha512-LJlKCCAUFKOuZxipUdYw1NzIITEsPcgyOG9LkvRJvuKj/bRzbuRY0ARXvL0pWGpsZvn8jku+LMep2eU0seHp8g==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prelude-ls@1.2.1:
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+ engines: {node: '>= 0.8.0'}
+
+ prettier-plugin-astro@0.14.1:
+ resolution: {integrity: sha512-RiBETaaP9veVstE4vUwSIcdATj6dKmXljouXc/DDNwBSPTp8FRkLGDSGFClKsAFeeg+13SB0Z1JZvbD76bigJw==}
+ engines: {node: ^14.15.0 || >=16.0.0}
+
+ prettier-plugin-tailwindcss@0.8.1:
+ resolution: {integrity: sha512-iaFMYqDsE4ffdDkn5qup0j5f2aCEBFZrdrZnvu9QKTlWx/iGPeQ4HHu7b7fCPMxeo9nwQBiOAh2nSypdFYWJkw==}
+ engines: {node: '>=20.19'}
+ peerDependencies:
+ '@ianvs/prettier-plugin-sort-imports': '*'
+ '@prettier/plugin-hermes': '*'
+ '@prettier/plugin-oxc': '*'
+ '@prettier/plugin-pug': '*'
+ '@shopify/prettier-plugin-liquid': '*'
+ '@trivago/prettier-plugin-sort-imports': '*'
+ '@zackad/prettier-plugin-twig': '*'
+ prettier: ^3.0
+ prettier-plugin-astro: '*'
+ prettier-plugin-css-order: '*'
+ prettier-plugin-jsdoc: '*'
+ prettier-plugin-marko: '*'
+ prettier-plugin-multiline-arrays: '*'
+ prettier-plugin-organize-attributes: '*'
+ prettier-plugin-organize-imports: '*'
+ prettier-plugin-sort-imports: '*'
+ prettier-plugin-svelte: '*'
+ peerDependenciesMeta:
+ '@ianvs/prettier-plugin-sort-imports':
+ optional: true
+ '@prettier/plugin-hermes':
+ optional: true
+ '@prettier/plugin-oxc':
+ optional: true
+ '@prettier/plugin-pug':
+ optional: true
+ '@shopify/prettier-plugin-liquid':
+ optional: true
+ '@trivago/prettier-plugin-sort-imports':
+ optional: true
+ '@zackad/prettier-plugin-twig':
+ optional: true
+ prettier-plugin-astro:
+ optional: true
+ prettier-plugin-css-order:
+ optional: true
+ prettier-plugin-jsdoc:
+ optional: true
+ prettier-plugin-marko:
+ optional: true
+ prettier-plugin-multiline-arrays:
+ optional: true
+ prettier-plugin-organize-attributes:
+ optional: true
+ prettier-plugin-organize-imports:
+ optional: true
+ prettier-plugin-sort-imports:
+ optional: true
+ prettier-plugin-svelte:
+ optional: true
+
+ prettier@3.9.10:
+ resolution: {integrity: sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ prismjs@1.30.0:
+ resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==}
+ engines: {node: '>=6'}
+
+ process-ancestry@0.1.0:
+ resolution: {integrity: sha512-tGqJW/UnclpYASFcM6Xh8D8l/BMtaQ9+CSG0vlJSJTcdMM4lDRv4c6H0Pdcsfted+bVczdYSfk2fdukg2gQkZg==}
+ engines: {node: '>=18.0.0'}
+
+ property-information@7.2.0:
+ resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==}
+
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ qified@0.10.1:
+ resolution: {integrity: sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==}
+ engines: {node: '>=20'}
+
+ radix3@1.1.2:
+ resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==}
+
+ readdirp@5.1.1:
+ resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==}
+ engines: {node: '>= 20.19.0'}
+
+ recma-build-jsx@1.0.0:
+ resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==}
+
+ recma-jsx@1.0.1:
+ resolution: {integrity: sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ recma-parse@1.0.0:
+ resolution: {integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==}
+
+ recma-stringify@1.0.0:
+ resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==}
+
+ regex-recursion@6.0.2:
+ resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==}
+
+ regex-utilities@2.3.0:
+ resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==}
+
+ regex@6.1.0:
+ resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==}
+
+ rehype-callouts@2.2.0:
+ resolution: {integrity: sha512-snwN1LkjLM3SuymqpsoLonoJ8gVy5yJN9WztBiyj8HAAj5Mv3Fez5vFzVwscLQdzMZo27xv2FycGzAGDkT4dNw==}
+ engines: {node: '>=18.0.0'}
+
+ rehype-raw@7.0.0:
+ resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==}
+
+ rehype-recma@1.0.0:
+ resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==}
+
+ rehype-stringify@10.0.1:
+ resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==}
+
+ remark-collapse@0.1.2:
+ resolution: {integrity: sha512-x2lhtaT4nR1Tkp/hftoeiMHbdAntxu5Q9D6lymwMqUtoF2pg3bqNaa8NO/8yyOVhddYk0d0dkhGc+avGUf0lZA==}
+
+ remark-gfm@4.0.1:
+ resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==}
+
+ remark-mdx@3.1.1:
+ resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==}
+
+ remark-parse@11.0.0:
+ resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
+
+ remark-rehype@11.1.2:
+ resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==}
+
+ remark-smartypants@3.0.3:
+ resolution: {integrity: sha512-gCaK+ndZ0hYezlqFegHFCVh2CQemsi0Npdh1qVM9bxlUFknjkbP6VmojWhddOCrbK0PbbacmYLWfTULRiT1eWA==}
+ engines: {node: '>=16.0.0'}
+
+ remark-stringify@11.0.0:
+ resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
+
+ remark-toc@9.0.0:
+ resolution: {integrity: sha512-KJ9txbo33GjDAV1baHFze7ij4G8c7SGYoY8Kzsm2gzFpbhL/bSoVpMMzGa3vrNDSWASNd/3ppAqL7cP2zD6JIA==}
+
+ request-light@0.5.8:
+ resolution: {integrity: sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==}
+
+ request-light@0.7.0:
+ resolution: {integrity: sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==}
+
+ require-from-string@2.0.2:
+ resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+ engines: {node: '>=0.10.0'}
+
+ resolve-pkg-maps@1.0.0:
+ resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+
+ retext-latin@4.0.0:
+ resolution: {integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==}
+
+ retext-smartypants@6.2.0:
+ resolution: {integrity: sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==}
+
+ retext-stringify@4.0.0:
+ resolution: {integrity: sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==}
+
+ retext@9.0.0:
+ resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==}
+
+ rolldown@1.2.15:
+ resolution: {integrity: sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+
+ s.color@0.0.15:
+ resolution: {integrity: sha512-AUNrbEUHeKY8XsYr/DYpl+qk5+aM+DChopnWOPEzn8YKzOhv4l2zH6LzZms3tOZP3wwdOyc0RmTciyi46HLIuA==}
+
+ sass-formatter@0.7.9:
+ resolution: {integrity: sha512-CWZ8XiSim+fJVG0cFLStwDvft1VI7uvXdCNJYXhDvowiv+DsbD1nXLiQ4zrE5UBvj5DWZJ93cwN0NX5PMsr1Pw==}
+
+ satori@0.63.2:
+ resolution: {integrity: sha512-h7dm4BRMo8F53l9vOXOIfJoqeVXuZvyz13VsvVuwJb72sLfq+8uddJIJcjLKLv2BqWzDvYIpzi3rvBBS+ETuvw==}
+ engines: {node: '>=16'}
+
+ satteri@0.10.5:
+ resolution: {integrity: sha512-Ao1LKpAEa9Wdg0otgbVKViZHEq9ebdXe4DMrp3s9vQAU0HNIuHnFEuMuOcm0ZIXyV0Yzxj91NvhLpvXZJO/5ZQ==}
+
+ sax@1.6.1:
+ resolution: {integrity: sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==}
+ engines: {node: '>=11.0.0'}
+
+ semver@7.8.5:
+ resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ sharp@0.35.5:
+ resolution: {integrity: sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==}
+ engines: {node: '>=20.9.0'}
+ peerDependencies:
+ '@types/node': '*'
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+
+ shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+
+ shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
+ shiki@4.5.0:
+ resolution: {integrity: sha512-OcRW72ngQvc+MSVWBRIovC/BTW589iUE/Vxav7VeM4RcY9MUjqB+IR6ILG1k9nuW6LiG/nct9uma4/MBtnZbmQ==}
+ engines: {node: '>=20'}
+
+ sisteransi@1.0.5:
+ resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
+
+ sitemap@9.0.1:
+ resolution: {integrity: sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==}
+ engines: {node: '>=20.19.5', npm: '>=10.8.2'}
+ hasBin: true
+
+ slugify@1.6.9:
+ resolution: {integrity: sha512-vZ7rfeehZui7wQs438JXBckYLkIIdfHOXsaVEUMyS5fHo1483l1bMdo0EDSWYclY0yZKFOipDy4KHuKs6ssvdg==}
+ engines: {node: '>=8.0.0'}
+
+ smol-toml@1.9.1:
+ resolution: {integrity: sha512-j7WyPdf0CJoutMzMoPLF/bVXHtKZIT9Rb4eGeEbWw8X2t5oirjPgpL+OpaENZrILYaXqYwnVH+qgUwVbl+pUJQ==}
+ engines: {node: '>= 18'}
+
+ source-map-js@1.2.2:
+ resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==}
+ engines: {node: '>=0.10.0'}
+
+ source-map@0.7.6:
+ resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==}
+ engines: {node: '>= 12'}
+
+ source-map@0.8.0:
+ resolution: {integrity: sha512-d8EqvL+k/SOXCreS/SUzg2ciyHqBBLcN/yuRjFsbvVhHTE2pgei7oAhmPM7kWFbkX6OSMQfUq4KbkF3au9lhYQ==}
+ engines: {node: '>= 12'}
+
+ space-separated-tokens@2.0.2:
+ resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
+
+ std-env@4.3.0:
+ resolution: {integrity: sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==}
+
+ string-width@7.2.0:
+ resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
+ engines: {node: '>=18'}
+
+ string-width@8.3.0:
+ resolution: {integrity: sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==}
+ engines: {node: '>=20'}
+
+ string.prototype.codepointat@0.2.1:
+ resolution: {integrity: sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==}
+
+ stringify-entities@4.0.4:
+ resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==}
+
+ strip-ansi@7.2.0:
+ resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==}
+ engines: {node: '>=12'}
+
+ strnum@2.5.1:
+ resolution: {integrity: sha512-UM4nTLWzQiDBseVxUGxN0wJzIk0UU89Qi7pKXz9qISo+lhviJikTuikBsK/oFRlpvYHvSdz0TVoLyQ+sEpYwxA==}
+
+ style-to-js@1.1.21:
+ resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==}
+
+ style-to-object@1.0.14:
+ resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==}
+
+ suf-log@2.5.3:
+ resolution: {integrity: sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow==}
+
+ svgo@4.1.0:
+ resolution: {integrity: sha512-bkxnTg1kSU0guhIBmibA6UUhrQmPVA1XsQLN+ylCd+UWzbnLkySOcXpyk1mrl05f+pcaCx2eHb+sp6BgMZWX+Q==}
+ engines: {node: '>=16'}
+ hasBin: true
+
+ synckit@0.11.13:
+ resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+
+ tailwindcss@4.3.3:
+ resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==}
+
+ tapable@2.3.3:
+ resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
+ engines: {node: '>=6'}
+
+ tiny-inflate@1.0.3:
+ resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==}
+
+ tinyclip@1.0.3:
+ resolution: {integrity: sha512-nTv2raKFMzGG6JJp0lpNDsRPdfFE6UHlu2rVEjXo8vkx1ZS3LW2CVXqIaLu8xZlu+6tbFs9V58Qc/Jmk/Ii7wA==}
+ engines: {node: ^16.14.0 || >= 17.3.0}
+
+ tinyexec@1.3.1:
+ resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==}
+ engines: {node: '>=18'}
+
+ tinyglobby@0.2.17:
+ resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
+ engines: {node: '>=12.0.0'}
+
+ tinyps@0.5.1:
+ resolution: {integrity: sha512-B6Ykif5HflC3AdDkS4Zst/2muYVfeXoVMy+KorqH80s/1x4vunIORpNySH681X7fjj+dFM9MDKD6/Hsccjb87Q==}
+
+ trim-lines@3.0.1:
+ resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
+
+ trough@2.2.0:
+ resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==}
+
+ ts-api-utils@2.5.0:
+ resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==}
+ engines: {node: '>=18.12'}
+ peerDependencies:
+ typescript: '>=4.8.4'
+
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+ type-check@0.4.0:
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+ engines: {node: '>= 0.8.0'}
+
+ typesafe-path@0.2.2:
+ resolution: {integrity: sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==}
+
+ typescript-auto-import-cache@0.3.6:
+ resolution: {integrity: sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ==}
+
+ typescript@6.0.3:
+ resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ ufo@1.6.4:
+ resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==}
+
+ ultrahtml@1.7.0:
+ resolution: {integrity: sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==}
+
+ uncrypto@0.1.3:
+ resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==}
+
+ undici-types@7.24.6:
+ resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==}
+
+ undici@8.12.3:
+ resolution: {integrity: sha512-+fdndUTwBP86cVjihTp21hZHo7d6oj1c/47XPA3lL8mM0XHs+C4EkcjmgRsFnFk2f/UONGDy9ItBItmXsLOQHw==}
+ engines: {node: '>=22.19.0'}
+
+ unicode-trie@2.0.0:
+ resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==}
+
+ unified@11.0.5:
+ resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
+
+ unifont@1.0.3:
+ resolution: {integrity: sha512-XGnEwj5sWdCbiBX01mWLSBncS0yMRjnLygau3IfdOlfjMacNIQvTGW4VH7pWBq+gGWArfSef/CensMLjMmIr/A==}
+
+ unist-util-is@6.0.1:
+ resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==}
+
+ unist-util-modify-children@4.0.0:
+ resolution: {integrity: sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==}
+
+ unist-util-position-from-estree@2.0.0:
+ resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==}
+
+ unist-util-position@5.0.0:
+ resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
+
+ unist-util-remove-position@5.0.0:
+ resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==}
+
+ unist-util-stringify-position@4.0.0:
+ resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
+
+ unist-util-visit-children@3.0.0:
+ resolution: {integrity: sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==}
+
+ unist-util-visit-parents@6.1.0:
+ resolution: {integrity: sha512-vKXxz6O38sKDj1mtx/k2cWvkkFBuYGx6uGrlzBkVeWCRF/F8sdI0j3Wbkz/5rVNRRuVc8UrgRdbCxNhmYXgP3w==}
+
+ unist-util-visit@5.2.0:
+ resolution: {integrity: sha512-fpsYs/VVoZxfGY9/j3FQpZmN8wdLJaV1Pt38AaJNZu9uKIHcFU3halCdBveEq1HGKO1OEfJY65w1zvoFV1b3qQ==}
+
+ unstorage@1.17.6:
+ resolution: {integrity: sha512-gjKtcmn/KwZuJZO9o2sc0esAQnhmTw0LviDHwevTXbJMYs0dRTp8ZEEWXHU0vTpzbN3cA0yxlTeyXssMp3zDqw==}
+ peerDependencies:
+ '@azure/app-configuration': ^1.8.0
+ '@azure/cosmos': ^4.2.0
+ '@azure/data-tables': ^13.3.0
+ '@azure/identity': ^4.6.0
+ '@azure/keyvault-secrets': ^4.9.0
+ '@azure/storage-blob': ^12.26.0
+ '@capacitor/preferences': ^6 || ^7 || ^8
+ '@deno/kv': '>=0.9.0'
+ '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 || ^11.0.0
+ '@planetscale/database': ^1.19.0
+ '@upstash/redis': ^1.34.3
+ '@vercel/blob': '>=0.27.1'
+ '@vercel/functions': ^2.2.12 || ^3.0.0
+ '@vercel/kv': ^1 || ^2 || ^3
+ 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
+
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+ util-deprecate@1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+ verkit@0.5.0:
+ resolution: {integrity: sha512-u4J1hr6Y+C/hit34GwL3eIcUf3wngT8TlYUjK7tk0jxydKLAq3sYvifjdTDA6U3KCKRcQCQDGf/srjxfZF3l7g==}
+ engines: {node: '>=18.12.0'}
+
+ vfile-location@5.0.3:
+ resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==}
+
+ vfile-message@4.0.3:
+ resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==}
+
+ vfile@6.0.3:
+ resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
+
+ vite@8.3.5:
+ resolution: {integrity: sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ 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
+
+ vitefu@1.1.3:
+ resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==}
+ peerDependencies:
+ vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0
+ peerDependenciesMeta:
+ vite:
+ optional: true
+
+ volar-service-css@0.0.71:
+ resolution: {integrity: sha512-wRRFt9BpjMKCazcgOh67MSjUjiWUCAh99DyYSDIOTuxaRjEtDC7PpB0k1Y1wbJIW/pVtMUSVbpPo3UGSm0Byxw==}
+ peerDependencies:
+ '@volar/language-service': ~2.4.0
+ peerDependenciesMeta:
+ '@volar/language-service':
+ optional: true
+
+ volar-service-emmet@0.0.71:
+ resolution: {integrity: sha512-zqjzt6bN95e3CUstBm0PBFAJnrfz0ZAARka87fart46/gNCLLuP3Vujy8V/J8HEziTFLnfkgIASLFYPUhonJcA==}
+ peerDependencies:
+ '@volar/language-service': ~2.4.0
+ peerDependenciesMeta:
+ '@volar/language-service':
+ optional: true
+
+ volar-service-html@0.0.71:
+ resolution: {integrity: sha512-e8tHPhgQ7ooLfudAEIku+kgd9pWkq3SSz8RbnQDI1+Eb8wbenkLGHqoirLqz5ORLV6wIMr2Iv08RWBG5eOcgpw==}
+ peerDependencies:
+ '@volar/language-service': ~2.4.0
+ peerDependenciesMeta:
+ '@volar/language-service':
+ optional: true
+
+ volar-service-prettier@0.0.71:
+ resolution: {integrity: sha512-Rz7JVH3qD108UCdmIEiZvOBNljMt2nLFdbN8AXcDfn7xD9F5I2aCIsDVqBbXw21PsnxG0b7MfwtNF+zPS/NKUg==}
+ peerDependencies:
+ '@volar/language-service': ~2.4.0
+ prettier: ^2.2 || ^3.0
+ peerDependenciesMeta:
+ '@volar/language-service':
+ optional: true
+ prettier:
+ optional: true
+
+ volar-service-typescript-twoslash-queries@0.0.71:
+ resolution: {integrity: sha512-9K2k72s4n7rV9s4bX0MyjbX9iBribvKZbBJKuEmTCZfeWJXs6Yh7bGpY4eoc7UufAjvpheBqwyZCOIPBvxCv0A==}
+ peerDependencies:
+ '@volar/language-service': ~2.4.0
+ peerDependenciesMeta:
+ '@volar/language-service':
+ optional: true
+
+ volar-service-typescript@0.0.71:
+ resolution: {integrity: sha512-yTtM/BVT6hoyEYnDtaCyAtNhdNeS/mhTTABlBOdw3NNiRBUin3IznFJpgfjer4c6RYopiPjjQjc9VFhxVl1mLw==}
+ peerDependencies:
+ '@volar/language-service': ~2.4.0
+ peerDependenciesMeta:
+ '@volar/language-service':
+ optional: true
+
+ volar-service-yaml@0.0.71:
+ resolution: {integrity: sha512-qYGWGuVpUTnZGu5P/CR4KLK4aIR8RrcVnmfZ2eRcj9q/I8VZCoC5yy9FtEvfNvnDp4MU17yhdJcvpQPIqhJS2Q==}
+ peerDependencies:
+ '@volar/language-service': ~2.4.0
+ peerDependenciesMeta:
+ '@volar/language-service':
+ optional: true
+
+ vscode-css-languageservice@6.3.10:
+ resolution: {integrity: sha512-eq5N9Er3fC4vA9zd9EFhyBG90wtCCuXgRSpAndaOgXMh1Wgep5lBgRIeDgjZBW9pa+332yC9+49cZMW8jcL3MA==}
+
+ vscode-html-languageservice@5.6.2:
+ resolution: {integrity: sha512-ulCrSnFnfQ16YzvwnYUgEbUEl/ZG7u2eV27YhvLObSHKkb8fw1Z9cgsnUwjTEeDIdJDoTDTDpxuhQwoenoLNMg==}
+
+ vscode-json-languageservice@4.1.8:
+ resolution: {integrity: sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg==}
+ engines: {npm: '>=7.0.0'}
+
+ vscode-jsonrpc@10.0.0:
+ resolution: {integrity: sha512-USSfxg0vDIpTQqlQBGxF3mBARyoS8cU7LHLpbYNh6dOJt32dlskxPWB0Irrh5TllTmyf7oNLAvqDqzklbwwnpA==}
+ engines: {node: '>=14.17.0'}
+
+ vscode-jsonrpc@8.2.0:
+ resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==}
+ engines: {node: '>=14.0.0'}
+
+ vscode-languageserver-protocol@3.17.5:
+ resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==}
+
+ vscode-languageserver-protocol@3.18.5:
+ resolution: {integrity: sha512-FW4fBMzZ99N8xeob4py0l050wgCoRgTpEq5Xcsb9lM1bc29UKGR/wSV+4knaqFxssFHajy7OzNiKP/m3U1ZEuA==}
+
+ vscode-languageserver-textdocument@1.0.16:
+ resolution: {integrity: sha512-qeNEuOi9a3qkqRf7Gfez2WL2VwlBTLCNekHBGO9kxPVUp9XuY15yfTJ9jwvOwKI0I2MhUUfZq95NSp+LK1CLQg==}
+
+ vscode-languageserver-types@3.17.5:
+ resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==}
+
+ vscode-languageserver-types@3.18.5:
+ resolution: {integrity: sha512-kPd6+JQNz3HMJvjKlqUxERSlZHAntl/aGpsk7ImwIh0ewDKDcjgAEyVSYciPiOJD3ftbK9IrfiqUeTZLSbCizw==}
+
+ vscode-languageserver@9.0.1:
+ resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==}
+ hasBin: true
+
+ vscode-nls@5.2.0:
+ resolution: {integrity: sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==}
+
+ vscode-uri@3.2.0:
+ resolution: {integrity: sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==}
+
+ web-namespaces@2.0.1:
+ resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
+ wrap-ansi@9.0.2:
+ resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==}
+ engines: {node: '>=18'}
+
+ xml-naming@0.3.0:
+ resolution: {integrity: sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==}
+ engines: {node: '>=16.0.0'}
+
+ xxhash-wasm@1.1.0:
+ resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==}
+
+ y18n@5.0.8:
+ resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+ engines: {node: '>=10'}
+
+ yaml-language-server@1.23.0:
+ resolution: {integrity: sha512-3qVyCOexLCWw06PQa5kRPwvMWMZ/eZeCRWUvgD6a0OkqL/4iCnxy2WumbWifa937Uo5xhyWJ0uxlU39ljhNh7A==}
+ hasBin: true
+
+ yaml@2.8.3:
+ resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==}
+ engines: {node: '>= 14.6'}
+ hasBin: true
+
+ yaml@2.9.1:
+ resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==}
+ engines: {node: '>= 14.6'}
+ hasBin: true
+
+ yargs-parser@22.0.0:
+ resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=23}
+
+ yargs@18.2.0:
+ resolution: {integrity: sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=23}
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+ yocto-queue@1.2.2:
+ resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==}
+ engines: {node: '>=12.20'}
+
+ zod@4.6.7:
+ resolution: {integrity: sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==}
+
+ zwitch@2.0.4:
+ resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
+
+snapshots:
+
+ '@astrojs/astro2tsx@0.1.3(@emnapi/runtime@1.11.3)':
+ dependencies:
+ '@emnapi/core': 1.11.3
+ '@napi-rs/wasm-runtime': 1.2.4(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)
+ transitivePeerDependencies:
+ - '@emnapi/runtime'
+
+ '@astrojs/check@0.9.12(@emnapi/runtime@1.11.3)(prettier-plugin-astro@0.14.1)(prettier@3.9.10)(typescript@6.0.3)':
+ dependencies:
+ '@astrojs/language-server': 2.17.3(@emnapi/runtime@1.11.3)(prettier-plugin-astro@0.14.1)(prettier@3.9.10)(typescript@6.0.3)
+ chokidar: 5.0.0
+ kleur: 4.1.5
+ typescript: 6.0.3
+ yargs: 18.2.0
+ transitivePeerDependencies:
+ - '@emnapi/runtime'
+ - prettier
+ - prettier-plugin-astro
+
+ '@astrojs/compiler-binding-android-arm64@0.5.2':
+ optional: true
+
+ '@astrojs/compiler-binding-darwin-arm64@0.5.2':
+ optional: true
+
+ '@astrojs/compiler-binding-darwin-x64@0.5.2':
+ optional: true
+
+ '@astrojs/compiler-binding-linux-arm64-gnu@0.5.2':
+ optional: true
+
+ '@astrojs/compiler-binding-linux-arm64-musl@0.5.2':
+ optional: true
+
+ '@astrojs/compiler-binding-linux-x64-gnu@0.5.2':
+ optional: true
+
+ '@astrojs/compiler-binding-linux-x64-musl@0.5.2':
+ optional: true
+
+ '@astrojs/compiler-binding-wasm32-wasi@0.5.2':
+ dependencies:
+ '@emnapi/core': 1.11.3
+ '@emnapi/runtime': 1.11.3
+ '@napi-rs/wasm-runtime': 1.2.6(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)
+ optional: true
+
+ '@astrojs/compiler-binding-win32-arm64-msvc@0.5.2':
+ optional: true
+
+ '@astrojs/compiler-binding-win32-x64-msvc@0.5.2':
+ optional: true
+
+ '@astrojs/compiler-binding@0.5.2':
+ 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
+
+ '@astrojs/compiler-rs@0.5.2':
+ dependencies:
+ '@astrojs/compiler-binding': 0.5.2
+
+ '@astrojs/compiler@2.13.1': {}
+
+ '@astrojs/internal-helpers@0.10.4':
+ dependencies:
+ '@types/hast': 3.0.5
+ '@types/mdast': 4.0.4
+ js-yaml: 4.3.2
+ picomatch: 4.0.7
+ retext-smartypants: 6.2.0
+ shiki: 4.5.0
+ smol-toml: 1.9.1
+ unified: 11.0.5
+
+ '@astrojs/internal-helpers@0.13.0':
+ 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
+
+ '@astrojs/language-server@2.17.3(@emnapi/runtime@1.11.3)(prettier-plugin-astro@0.14.1)(prettier@3.9.10)(typescript@6.0.3)':
+ dependencies:
+ '@astrojs/astro2tsx': 0.1.3(@emnapi/runtime@1.11.3)
+ '@astrojs/yaml2ts': 0.2.4
+ '@volar/kit': 2.4.30(typescript@6.0.3)
+ '@volar/language-core': 2.4.30
+ '@volar/language-server': 2.4.30(typescript@6.0.3)
+ '@volar/language-service': 2.4.30(typescript@6.0.3)
+ muggle-string: 0.4.1
+ tinyglobby: 0.2.17
+ volar-service-css: 0.0.71(@volar/language-service@2.4.30(typescript@6.0.3))
+ volar-service-emmet: 0.0.71(@volar/language-service@2.4.30(typescript@6.0.3))
+ volar-service-html: 0.0.71(@volar/language-service@2.4.30(typescript@6.0.3))
+ volar-service-prettier: 0.0.71(@volar/language-service@2.4.30(typescript@6.0.3))(prettier@3.9.10)
+ volar-service-typescript: 0.0.71(@volar/language-service@2.4.30(typescript@6.0.3))
+ volar-service-typescript-twoslash-queries: 0.0.71(@volar/language-service@2.4.30(typescript@6.0.3))
+ volar-service-yaml: 0.0.71(@volar/language-service@2.4.30(typescript@6.0.3))
+ vscode-html-languageservice: 5.6.2
+ vscode-uri: 3.2.0
+ optionalDependencies:
+ prettier: 3.9.10
+ prettier-plugin-astro: 0.14.1
+ transitivePeerDependencies:
+ - '@emnapi/runtime'
+ - typescript
+
+ '@astrojs/markdown-remark@7.2.4':
+ dependencies:
+ '@astrojs/internal-helpers': 0.10.4
+ '@astrojs/prism': 4.0.2
+ github-slugger: 2.0.0
+ hast-util-from-html: 2.0.3
+ hast-util-to-text: 4.0.3
+ mdast-util-definitions: 6.0.0
+ rehype-raw: 7.0.0
+ rehype-stringify: 10.0.1
+ remark-gfm: 4.0.1
+ remark-parse: 11.0.0
+ remark-rehype: 11.1.2
+ remark-smartypants: 3.0.3
+ unified: 11.0.5
+ unist-util-remove-position: 5.0.0
+ unist-util-visit: 5.2.0
+ unist-util-visit-parents: 6.1.0
+ vfile: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@astrojs/markdown-remark@7.3.5':
+ dependencies:
+ '@astrojs/internal-helpers': 0.13.0
+ '@astrojs/prism': 4.1.2
+ '@mdx-js/mdx': 3.1.1
+ acorn: 8.19.0
+ estree-util-visit: 2.0.0
+ github-slugger: 2.0.0
+ hast-util-from-html: 2.0.3
+ hast-util-to-html: 9.0.5
+ hast-util-to-text: 4.0.3
+ mdast-util-definitions: 6.0.0
+ rehype-raw: 7.0.0
+ rehype-stringify: 10.0.1
+ remark-gfm: 4.0.1
+ remark-parse: 11.0.0
+ remark-rehype: 11.1.2
+ remark-smartypants: 3.0.3
+ source-map: 0.8.0
+ unified: 11.0.5
+ unist-util-remove-position: 5.0.0
+ unist-util-visit: 5.2.0
+ unist-util-visit-parents: 6.1.0
+ vfile: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@astrojs/markdown-satteri@0.4.6':
+ dependencies:
+ '@astrojs/internal-helpers': 0.13.0
+ '@astrojs/prism': 4.1.2
+ github-slugger: 2.0.0
+ satteri: 0.10.5
+
+ '@astrojs/mdx@7.0.8(astro@7.4.5(@astrojs/markdown-remark@7.3.5)(@types/node@24.19.2)(jiti@2.7.0)(yaml@2.9.1))':
+ dependencies:
+ '@astrojs/internal-helpers': 0.10.4
+ '@astrojs/markdown-remark': 7.2.4
+ '@mdx-js/mdx': 3.1.1
+ acorn: 8.19.0
+ astro: 7.4.5(@astrojs/markdown-remark@7.3.5)(@types/node@24.19.2)(jiti@2.7.0)(yaml@2.9.1)
+ es-module-lexer: 2.3.2
+ estree-util-visit: 2.0.0
+ hast-util-to-html: 9.0.5
+ piccolore: 0.1.3
+ rehype-raw: 7.0.0
+ remark-gfm: 4.0.1
+ remark-smartypants: 3.0.3
+ source-map: 0.7.6
+ unist-util-visit: 5.2.0
+ vfile: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@astrojs/prism@4.0.2':
+ dependencies:
+ prismjs: 1.30.0
+
+ '@astrojs/prism@4.1.2':
+ dependencies:
+ prismjs: 1.30.0
+
+ '@astrojs/rss@4.0.19':
+ dependencies:
+ fast-xml-parser: 5.11.2
+ piccolore: 0.1.3
+ zod: 4.6.7
+
+ '@astrojs/sitemap@3.7.4':
+ dependencies:
+ sitemap: 9.0.1
+ zod: 4.6.7
+
+ '@astrojs/telemetry@3.4.2':
+ dependencies:
+ ci-info: 4.4.0
+ dset: 3.1.4
+ is-docker: 4.0.0
+ package-manager-detector: 1.9.0
+
+ '@astrojs/yaml2ts@0.2.4':
+ dependencies:
+ yaml: 2.9.1
+
+ '@babel/helper-string-parser@7.29.11': {}
+
+ '@babel/helper-validator-identifier@7.29.7': {}
+
+ '@babel/parser@7.29.11':
+ dependencies:
+ '@babel/types': 7.29.11
+
+ '@babel/types@7.29.11':
+ dependencies:
+ '@babel/helper-string-parser': 7.29.11
+ '@babel/helper-validator-identifier': 7.29.7
+
+ '@bruits/satteri-darwin-arm64@0.10.5':
+ optional: true
+
+ '@bruits/satteri-darwin-x64@0.10.5':
+ optional: true
+
+ '@bruits/satteri-linux-arm64-gnu@0.10.5':
+ optional: true
+
+ '@bruits/satteri-linux-arm64-musl@0.10.5':
+ optional: true
+
+ '@bruits/satteri-linux-x64-gnu@0.10.5':
+ optional: true
+
+ '@bruits/satteri-linux-x64-musl@0.10.5':
+ optional: true
+
+ '@bruits/satteri-wasm32-wasi@0.10.5':
+ dependencies:
+ '@emnapi/core': 1.11.1
+ '@emnapi/runtime': 1.11.1
+ '@napi-rs/wasm-runtime': 1.2.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
+ optional: true
+
+ '@bruits/satteri-win32-arm64-msvc@0.10.5':
+ optional: true
+
+ '@bruits/satteri-win32-x64-msvc@0.10.5':
+ optional: true
+
+ '@cacheable/memory@2.2.0':
+ dependencies:
+ '@cacheable/utils': 2.5.0
+ '@keyv/bigmap': 1.3.1(keyv@5.6.0)
+ hookified: 1.15.1
+ keyv: 5.6.0
+
+ '@cacheable/utils@2.5.0':
+ dependencies:
+ hashery: 1.5.1
+ keyv: 5.6.0
+
+ '@capsizecss/unpack@4.0.1':
+ dependencies:
+ fontkitten: 1.0.3
+
+ '@clack/core@1.5.1':
+ dependencies:
+ fast-wrap-ansi: 0.2.2
+ sisteransi: 1.0.5
+
+ '@clack/prompts@1.8.1':
+ dependencies:
+ '@clack/core': 1.5.1
+ fast-string-width: 3.0.2
+ fast-wrap-ansi: 0.2.2
+ sisteransi: 1.0.5
+
+ '@emmetio/abbreviation@2.3.3':
+ dependencies:
+ '@emmetio/scanner': 1.0.4
+
+ '@emmetio/css-abbreviation@2.1.8':
+ dependencies:
+ '@emmetio/scanner': 1.0.4
+
+ '@emmetio/css-parser@0.4.1':
+ dependencies:
+ '@emmetio/stream-reader': 2.2.0
+ '@emmetio/stream-reader-utils': 0.1.0
+
+ '@emmetio/html-matcher@1.3.0':
+ dependencies:
+ '@emmetio/scanner': 1.0.4
+
+ '@emmetio/scanner@1.0.4': {}
+
+ '@emmetio/stream-reader-utils@0.1.0': {}
+
+ '@emmetio/stream-reader@2.2.0': {}
+
+ '@emnapi/core@1.11.1':
+ dependencies:
+ '@emnapi/wasi-threads': 1.2.2
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/core@1.11.3':
+ dependencies:
+ '@emnapi/wasi-threads': 1.2.3
+ tslib: 2.8.1
+
+ '@emnapi/runtime@1.11.1':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/runtime@1.11.3':
+ dependencies:
+ tslib: 2.8.1
+
+ '@emnapi/wasi-threads@1.2.2':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/wasi-threads@1.2.3':
+ dependencies:
+ tslib: 2.8.1
+
+ '@eslint-community/eslint-utils@4.10.1(eslint@10.14.0(jiti@2.7.0))':
+ dependencies:
+ eslint: 10.14.0(jiti@2.7.0)
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/regexpp@4.12.2': {}
+
+ '@eslint/config-array@0.23.5':
+ dependencies:
+ '@eslint/object-schema': 3.0.5
+ debug: 4.4.3
+ minimatch: 10.2.6
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/config-helpers@0.7.0':
+ dependencies:
+ '@eslint/core': 1.2.1
+
+ '@eslint/core@1.2.1':
+ dependencies:
+ '@types/json-schema': 7.0.15
+
+ '@eslint/object-schema@3.0.5': {}
+
+ '@eslint/plugin-kit@0.7.3':
+ dependencies:
+ '@eslint/core': 1.2.1
+ levn: 0.4.1
+
+ '@humanfs/core@0.19.2':
+ dependencies:
+ '@humanfs/types': 0.15.0
+
+ '@humanfs/node@0.16.8':
+ dependencies:
+ '@humanfs/core': 0.19.2
+ '@humanfs/types': 0.15.0
+ '@humanwhocodes/retry': 0.4.3
+
+ '@humanfs/types@0.15.0': {}
+
+ '@humanwhocodes/module-importer@1.0.1': {}
+
+ '@humanwhocodes/retry@0.4.3': {}
+
+ '@img/colour@1.1.0': {}
+
+ '@img/sharp-darwin-arm64@0.35.5':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-arm64': 1.3.4
+ optional: true
+
+ '@img/sharp-darwin-x64@0.35.5':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-x64': 1.3.4
+ optional: true
+
+ '@img/sharp-freebsd-wasm32@0.35.5':
+ dependencies:
+ '@img/sharp-wasm32': 0.35.5
+ optional: true
+
+ '@img/sharp-libvips-darwin-arm64@1.3.4':
+ optional: true
+
+ '@img/sharp-libvips-darwin-x64@1.3.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm64@1.3.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm@1.3.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-ppc64@1.3.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-riscv64@1.3.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-s390x@1.3.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-x64@1.3.4':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.3.4':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-x64@1.3.4':
+ optional: true
+
+ '@img/sharp-linux-arm64@0.35.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm64': 1.3.4
+ optional: true
+
+ '@img/sharp-linux-arm@0.35.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm': 1.3.4
+ optional: true
+
+ '@img/sharp-linux-ppc64@0.35.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-ppc64': 1.3.4
+ optional: true
+
+ '@img/sharp-linux-riscv64@0.35.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-riscv64': 1.3.4
+ optional: true
+
+ '@img/sharp-linux-s390x@0.35.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-s390x': 1.3.4
+ optional: true
+
+ '@img/sharp-linux-x64@0.35.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-x64': 1.3.4
+ optional: true
+
+ '@img/sharp-linuxmusl-arm64@0.35.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-arm64': 1.3.4
+ optional: true
+
+ '@img/sharp-linuxmusl-x64@0.35.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-x64': 1.3.4
+ optional: true
+
+ '@img/sharp-wasm32@0.35.5':
+ dependencies:
+ '@emnapi/runtime': 1.11.3
+ optional: true
+
+ '@img/sharp-webcontainers-wasm32@0.35.5':
+ dependencies:
+ '@img/sharp-wasm32': 0.35.5
+ optional: true
+
+ '@img/sharp-win32-arm64@0.35.5':
+ optional: true
+
+ '@img/sharp-win32-ia32@0.35.5':
+ optional: true
+
+ '@img/sharp-win32-x64@0.35.5':
+ optional: true
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.6.0
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.6.0': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.6.0
+
+ '@keyv/bigmap@1.3.1(keyv@5.6.0)':
+ dependencies:
+ hashery: 1.5.1
+ hookified: 1.15.1
+ keyv: 5.6.0
+
+ '@keyv/serialize@1.1.1': {}
+
+ '@mdx-js/mdx@3.1.1':
+ dependencies:
+ '@types/estree': 1.0.9
+ '@types/estree-jsx': 1.0.5
+ '@types/hast': 3.0.5
+ '@types/mdx': 2.0.14
+ acorn: 8.19.0
+ collapse-white-space: 2.1.0
+ devlop: 1.1.0
+ estree-util-is-identifier-name: 3.0.0
+ estree-util-scope: 1.0.1
+ estree-walker: 3.0.3
+ hast-util-to-jsx-runtime: 2.3.6
+ markdown-extensions: 2.0.0
+ recma-build-jsx: 1.0.0
+ recma-jsx: 1.0.1(acorn@8.19.0)
+ recma-stringify: 1.0.0
+ rehype-recma: 1.0.0
+ remark-mdx: 3.1.1
+ remark-parse: 11.0.0
+ remark-rehype: 11.1.2
+ source-map: 0.7.6
+ unified: 11.0.5
+ unist-util-position-from-estree: 2.0.0
+ unist-util-stringify-position: 4.0.0
+ unist-util-visit: 5.2.0
+ vfile: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@napi-rs/wasm-runtime@1.2.4(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)':
+ dependencies:
+ '@emnapi/core': 1.11.3
+ '@emnapi/runtime': 1.11.3
+ '@tybys/wasm-util': 0.10.4
+
+ '@napi-rs/wasm-runtime@1.2.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
+ dependencies:
+ '@emnapi/core': 1.11.1
+ '@emnapi/runtime': 1.11.1
+ '@tybys/wasm-util': 0.10.4
+ optional: true
+
+ '@napi-rs/wasm-runtime@1.2.6(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)':
+ dependencies:
+ '@emnapi/core': 1.11.3
+ '@emnapi/runtime': 1.11.3
+ '@tybys/wasm-util': 0.10.4
+ optional: true
+
+ '@nodable/entities@3.1.0': {}
+
+ '@oslojs/encoding@1.1.0': {}
+
+ '@oxc-project/types@0.155.0': {}
+
+ '@pagefind/darwin-arm64@1.6.0':
+ optional: true
+
+ '@pagefind/darwin-x64@1.6.0':
+ optional: true
+
+ '@pagefind/default-ui@1.6.0': {}
+
+ '@pagefind/freebsd-x64@1.6.0':
+ optional: true
+
+ '@pagefind/linux-arm64@1.6.0':
+ optional: true
+
+ '@pagefind/linux-x64@1.6.0':
+ optional: true
+
+ '@pagefind/windows-arm64@1.6.0':
+ optional: true
+
+ '@pagefind/windows-x64@1.6.0':
+ optional: true
+
+ '@pkgr/core@0.3.6': {}
+
+ '@rolldown/binding-android-arm-eabi@1.2.15':
+ optional: true
+
+ '@rolldown/binding-android-arm64@1.2.15':
+ optional: true
+
+ '@rolldown/binding-darwin-arm64@1.2.15':
+ optional: true
+
+ '@rolldown/binding-darwin-x64@1.2.15':
+ optional: true
+
+ '@rolldown/binding-freebsd-x64@1.2.15':
+ optional: true
+
+ '@rolldown/binding-linux-arm-gnueabihf@1.2.15':
+ optional: true
+
+ '@rolldown/binding-linux-arm64-gnu@1.2.15':
+ optional: true
+
+ '@rolldown/binding-linux-arm64-musl@1.2.15':
+ optional: true
+
+ '@rolldown/binding-linux-ppc64-gnu@1.2.15':
+ optional: true
+
+ '@rolldown/binding-linux-s390x-gnu@1.2.15':
+ optional: true
+
+ '@rolldown/binding-linux-x64-gnu@1.2.15':
+ optional: true
+
+ '@rolldown/binding-linux-x64-musl@1.2.15':
+ optional: true
+
+ '@rolldown/binding-openharmony-arm64@1.2.15':
+ optional: true
+
+ '@rolldown/binding-win32-arm64-msvc@1.2.15':
+ optional: true
+
+ '@rolldown/binding-win32-x64-msvc@1.2.15':
+ optional: true
+
+ '@rolldown/pluginutils@1.0.1': {}
+
+ '@shikijs/core@4.5.0':
+ dependencies:
+ '@shikijs/primitive': 4.5.0
+ '@shikijs/types': 4.5.0
+ '@shikijs/vscode-textmate': 10.1.0
+ '@types/hast': 3.0.5
+ hast-util-to-html: 9.0.5
+
+ '@shikijs/core@4.6.0':
+ dependencies:
+ '@shikijs/primitive': 4.6.0
+ '@shikijs/types': 4.6.0
+ '@shikijs/vscode-textmate': 10.1.0
+ '@types/hast': 3.0.5
+ hast-util-to-html: 9.0.6
+
+ '@shikijs/engine-javascript@4.5.0':
+ dependencies:
+ '@shikijs/types': 4.5.0
+ '@shikijs/vscode-textmate': 10.1.0
+ oniguruma-to-es: 4.3.6
+
+ '@shikijs/engine-oniguruma@4.5.0':
+ dependencies:
+ '@shikijs/types': 4.5.0
+ '@shikijs/vscode-textmate': 10.1.0
+
+ '@shikijs/langs@4.5.0':
+ dependencies:
+ '@shikijs/types': 4.5.0
+
+ '@shikijs/primitive@4.5.0':
+ dependencies:
+ '@shikijs/types': 4.5.0
+ '@shikijs/vscode-textmate': 10.1.0
+ '@types/hast': 3.0.5
+
+ '@shikijs/primitive@4.6.0':
+ dependencies:
+ '@shikijs/types': 4.6.0
+ '@shikijs/vscode-textmate': 10.1.0
+ '@types/hast': 3.0.5
+
+ '@shikijs/themes@4.5.0':
+ dependencies:
+ '@shikijs/types': 4.5.0
+
+ '@shikijs/transformers@4.6.0':
+ dependencies:
+ '@shikijs/core': 4.6.0
+ '@shikijs/types': 4.6.0
+
+ '@shikijs/types@4.5.0':
+ dependencies:
+ '@shikijs/vscode-textmate': 10.1.0
+ '@types/hast': 3.0.5
+
+ '@shikijs/types@4.6.0':
+ dependencies:
+ '@shikijs/vscode-textmate': 10.1.0
+ '@types/hast': 3.0.5
+
+ '@shikijs/vscode-textmate@10.1.0': {}
+
+ '@shuding/opentype.js@1.4.0-beta.0':
+ dependencies:
+ fflate: 0.7.5
+ string.prototype.codepointat: 0.2.1
+
+ '@tailwindcss/node@4.3.3':
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ enhanced-resolve: 5.26.0
+ jiti: 2.7.0
+ lightningcss: 1.32.0
+ magic-string: 0.30.21
+ source-map-js: 1.2.2
+ tailwindcss: 4.3.3
+
+ '@tailwindcss/oxide-android-arm64@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-x64@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-freebsd-x64@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-musl@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-wasm32-wasi@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide@4.3.3':
+ optionalDependencies:
+ '@tailwindcss/oxide-android-arm64': 4.3.3
+ '@tailwindcss/oxide-darwin-arm64': 4.3.3
+ '@tailwindcss/oxide-darwin-x64': 4.3.3
+ '@tailwindcss/oxide-freebsd-x64': 4.3.3
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.3
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.3.3
+ '@tailwindcss/oxide-linux-arm64-musl': 4.3.3
+ '@tailwindcss/oxide-linux-x64-gnu': 4.3.3
+ '@tailwindcss/oxide-linux-x64-musl': 4.3.3
+ '@tailwindcss/oxide-wasm32-wasi': 4.3.3
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.3.3
+ '@tailwindcss/oxide-win32-x64-msvc': 4.3.3
+
+ '@tailwindcss/typography@0.5.20(tailwindcss@4.3.3)':
+ dependencies:
+ postcss-selector-parser: 6.0.10
+ tailwindcss: 4.3.3
+
+ '@tailwindcss/vite@4.3.3(vite@8.3.5(@types/node@24.19.2)(jiti@2.7.0)(yaml@2.9.1))':
+ dependencies:
+ '@tailwindcss/node': 4.3.3
+ '@tailwindcss/oxide': 4.3.3
+ tailwindcss: 4.3.3
+ vite: 8.3.5(@types/node@24.19.2)(jiti@2.7.0)(yaml@2.9.1)
+
+ '@tybys/wasm-util@0.10.4':
+ dependencies:
+ tslib: 2.8.1
+
+ '@types/debug@4.1.13':
+ dependencies:
+ '@types/ms': 2.1.0
+
+ '@types/esrecurse@4.3.1': {}
+
+ '@types/estree-jsx@1.0.5':
+ dependencies:
+ '@types/estree': 1.0.9
+
+ '@types/estree@1.0.9': {}
+
+ '@types/hast@3.0.5':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/json-schema@7.0.15': {}
+
+ '@types/lodash.kebabcase@4.1.9':
+ dependencies:
+ '@types/lodash': 4.17.27
+
+ '@types/lodash@4.17.27': {}
+
+ '@types/mdast@4.0.4':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/mdx@2.0.14': {}
+
+ '@types/ms@2.1.0': {}
+
+ '@types/nlcst@2.0.3':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/node@24.19.2':
+ dependencies:
+ undici-types: 7.24.6
+
+ '@types/sax@1.2.7':
+ dependencies:
+ '@types/node': 24.19.2
+
+ '@types/ungap__structured-clone@1.2.0': {}
+
+ '@types/unist@2.0.11': {}
+
+ '@types/unist@3.0.3': {}
+
+ '@typescript-eslint/parser@8.72.1(eslint@10.14.0(jiti@2.7.0))(typescript@6.0.3)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.72.1
+ '@typescript-eslint/types': 8.72.1
+ '@typescript-eslint/typescript-estree': 8.72.1(typescript@6.0.3)
+ '@typescript-eslint/visitor-keys': 8.72.1
+ debug: 4.4.3
+ eslint: 10.14.0(jiti@2.7.0)
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/project-service@8.72.1(typescript@6.0.3)':
+ dependencies:
+ '@typescript-eslint/tsconfig-utils': 8.72.1(typescript@6.0.3)
+ '@typescript-eslint/types': 8.72.1
+ debug: 4.4.3
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/scope-manager@8.72.1':
+ dependencies:
+ '@typescript-eslint/types': 8.72.1
+ '@typescript-eslint/visitor-keys': 8.72.1
+
+ '@typescript-eslint/tsconfig-utils@8.72.1(typescript@6.0.3)':
+ dependencies:
+ typescript: 6.0.3
+
+ '@typescript-eslint/types@8.72.1': {}
+
+ '@typescript-eslint/typescript-estree@8.72.1(typescript@6.0.3)':
+ dependencies:
+ '@typescript-eslint/project-service': 8.72.1(typescript@6.0.3)
+ '@typescript-eslint/tsconfig-utils': 8.72.1(typescript@6.0.3)
+ '@typescript-eslint/types': 8.72.1
+ '@typescript-eslint/visitor-keys': 8.72.1
+ debug: 4.4.3
+ minimatch: 10.2.6
+ semver: 7.8.5
+ tinyglobby: 0.2.17
+ ts-api-utils: 2.5.0(typescript@6.0.3)
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/visitor-keys@8.72.1':
+ dependencies:
+ '@typescript-eslint/types': 8.72.1
+ eslint-visitor-keys: 5.0.1
+
+ '@ungap/structured-clone@1.4.0': {}
+
+ '@volar/kit@2.4.30(typescript@6.0.3)':
+ dependencies:
+ '@volar/language-service': 2.4.30(typescript@6.0.3)
+ '@volar/typescript': 2.4.30(typescript@6.0.3)
+ typesafe-path: 0.2.2
+ typescript: 6.0.3
+ vscode-languageserver-textdocument: 1.0.16
+ vscode-uri: 3.2.0
+
+ '@volar/language-core@2.4.30':
+ dependencies:
+ '@volar/source-map': 2.4.30
+
+ '@volar/language-server@2.4.30(typescript@6.0.3)':
+ dependencies:
+ '@volar/language-core': 2.4.30
+ '@volar/language-service': 2.4.30(typescript@6.0.3)
+ '@volar/typescript': 2.4.30(typescript@6.0.3)
+ path-browserify: 1.0.1
+ request-light: 0.7.0
+ vscode-languageserver: 9.0.1
+ vscode-languageserver-protocol: 3.18.5
+ vscode-languageserver-textdocument: 1.0.16
+ vscode-uri: 3.2.0
+ optionalDependencies:
+ typescript: 6.0.3
+
+ '@volar/language-service@2.4.30(typescript@6.0.3)':
+ dependencies:
+ '@volar/language-core': 2.4.30
+ vscode-languageserver-protocol: 3.18.5
+ vscode-languageserver-textdocument: 1.0.16
+ vscode-uri: 3.2.0
+ optionalDependencies:
+ typescript: 6.0.3
+
+ '@volar/source-map@2.4.30': {}
+
+ '@volar/typescript@2.4.30(typescript@6.0.3)':
+ dependencies:
+ '@volar/language-core': 2.4.30
+ '@volar/language-service': 2.4.30(typescript@6.0.3)
+ path-browserify: 1.0.1
+ vscode-uri: 3.2.0
+ optionalDependencies:
+ typescript: 6.0.3
+
+ '@vscode/emmet-helper@2.11.0':
+ dependencies:
+ emmet: 2.4.11
+ jsonc-parser: 2.3.1
+ vscode-languageserver-textdocument: 1.0.16
+ vscode-languageserver-types: 3.18.5
+ vscode-uri: 3.2.0
+
+ '@vscode/l10n@0.0.18': {}
+
+ acorn-jsx@5.3.2(acorn@8.19.0):
+ dependencies:
+ acorn: 8.19.0
+
+ acorn@8.19.0: {}
+
+ ajv-draft-04@1.0.0(ajv@8.20.0):
+ optionalDependencies:
+ ajv: 8.20.0
+
+ ajv-i18n@4.2.0(ajv@8.20.0):
+ dependencies:
+ ajv: 8.20.0
+
+ ajv@6.15.0:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+
+ ajv@8.20.0:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-uri: 3.1.9
+ json-schema-traverse: 1.0.0
+ require-from-string: 2.0.2
+
+ am-i-vibing@0.5.0:
+ dependencies:
+ process-ancestry: 0.1.0
+
+ ansi-regex@6.4.0: {}
+
+ ansi-styles@6.2.3: {}
+
+ anymatch@3.1.3:
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.2
+
+ anynum@1.0.1: {}
+
+ arg@5.0.2: {}
+
+ argparse@2.0.1: {}
+
+ aria-query@5.3.2: {}
+
+ array-iterate@2.0.1: {}
+
+ astring@1.9.0: {}
+
+ astro-eslint-parser@3.2.0:
+ dependencies:
+ '@astrojs/compiler-rs': 0.5.2
+ '@typescript-eslint/scope-manager': 8.72.1
+ '@typescript-eslint/types': 8.72.1
+ debug: 4.4.3
+ eslint-scope: 9.1.2
+ eslint-visitor-keys: 5.0.1
+ espree: 11.2.0
+ semver: 7.8.5
+ tinyglobby: 0.2.17
+ transitivePeerDependencies:
+ - supports-color
+
+ astro@7.4.5(@astrojs/markdown-remark@7.3.5)(@types/node@24.19.2)(jiti@2.7.0)(yaml@2.9.1):
+ dependencies:
+ '@astrojs/compiler-rs': 0.5.2
+ '@astrojs/internal-helpers': 0.13.0
+ '@astrojs/markdown-satteri': 0.4.6
+ '@astrojs/telemetry': 3.4.2
+ '@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.3
+ diff: 9.0.0
+ dset: 3.1.4
+ es-module-lexer: 3.0.4
+ 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.5
+ 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.15
+ 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.1
+ ultrahtml: 1.7.0
+ unifont: 1.0.3
+ unstorage: 1.17.6
+ verkit: 0.5.0
+ vite: 8.3.5(@types/node@24.19.2)(jiti@2.7.0)(yaml@2.9.1)
+ vitefu: 1.1.3(vite@8.3.5(@types/node@24.19.2)(jiti@2.7.0)(yaml@2.9.1))
+ xxhash-wasm: 1.1.0
+ yargs-parser: 22.0.0
+ zod: 4.6.7
+ optionalDependencies:
+ '@astrojs/markdown-remark': 7.3.5
+ sharp: 0.35.5(@types/node@24.19.2)
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@types/node'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/functions'
+ - '@vercel/kv'
+ - '@vitejs/devtools'
+ - aws4fetch
+ - db0
+ - esbuild
+ - idb-keyval
+ - ioredis
+ - jiti
+ - less
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - terser
+ - tsx
+ - uploadthing
+ - yaml
+
+ axobject-query@4.1.0: {}
+
+ bail@2.0.2: {}
+
+ balanced-match@4.0.4: {}
+
+ base64-js@0.0.8: {}
+
+ boolbase@1.0.0: {}
+
+ brace-expansion@5.0.12:
+ dependencies:
+ balanced-match: 4.0.4
+
+ cacheable@2.5.0:
+ dependencies:
+ '@cacheable/memory': 2.2.0
+ '@cacheable/utils': 2.5.0
+ hookified: 1.15.1
+ keyv: 5.6.0
+ qified: 0.10.1
+
+ camelize@1.0.1: {}
+
+ ccount@2.0.1: {}
+
+ character-entities-html4@2.1.0: {}
+
+ character-entities-legacy@3.0.0: {}
+
+ character-entities@2.0.2: {}
+
+ character-reference-invalid@2.0.1: {}
+
+ chokidar@5.0.0:
+ dependencies:
+ readdirp: 5.1.1
+
+ ci-info@4.4.0: {}
+
+ cliui@9.0.1:
+ dependencies:
+ string-width: 7.2.0
+ strip-ansi: 7.2.0
+ wrap-ansi: 9.0.2
+
+ clsx@2.1.1: {}
+
+ collapse-white-space@2.1.0: {}
+
+ color-name@1.1.4: {}
+
+ comma-separated-tokens@2.0.3: {}
+
+ commander@11.1.0: {}
+
+ common-ancestor-path@2.0.0: {}
+
+ cookie-es@1.2.3: {}
+
+ cookie@2.0.1: {}
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ crossws@0.3.5:
+ dependencies:
+ uncrypto: 0.1.3
+
+ css-background-parser@0.1.0: {}
+
+ css-box-shadow@1.0.0-3: {}
+
+ css-color-keywords@1.0.0: {}
+
+ css-gradient-parser@0.0.17: {}
+
+ css-select@6.0.0:
+ dependencies:
+ boolbase: 1.0.0
+ css-what: 7.0.0
+ domhandler: 5.0.3
+ domutils: 3.2.2
+ nth-check: 2.1.1
+
+ css-to-react-native@3.2.0:
+ dependencies:
+ camelize: 1.0.1
+ css-color-keywords: 1.0.0
+ postcss-value-parser: 4.2.0
+
+ css-tree@2.2.1:
+ dependencies:
+ mdn-data: 2.0.28
+ source-map-js: 1.2.2
+
+ css-tree@3.2.1:
+ dependencies:
+ mdn-data: 2.27.1
+ source-map-js: 1.2.2
+
+ css-what@7.0.0: {}
+
+ cssesc@3.0.0: {}
+
+ csso@5.0.5:
+ dependencies:
+ css-tree: 2.2.1
+
+ dayjs@1.11.23: {}
+
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ decode-named-character-reference@1.3.0:
+ dependencies:
+ character-entities: 2.0.2
+
+ deep-is@0.1.4: {}
+
+ defu@6.1.7: {}
+
+ dequal@2.0.3: {}
+
+ destr@2.0.5: {}
+
+ detect-libc@2.1.2: {}
+
+ devalue@6.0.3: {}
+
+ devlop@1.1.0:
+ dependencies:
+ dequal: 2.0.3
+
+ diff@9.0.0: {}
+
+ dom-serializer@2.0.0:
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ entities: 4.5.0
+
+ domelementtype@2.3.0: {}
+
+ domhandler@5.0.3:
+ dependencies:
+ domelementtype: 2.3.0
+
+ domutils@3.2.2:
+ dependencies:
+ dom-serializer: 2.0.0
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+
+ dset@3.1.4: {}
+
+ emmet@2.4.11:
+ dependencies:
+ '@emmetio/abbreviation': 2.3.3
+ '@emmetio/css-abbreviation': 2.1.8
+
+ emoji-regex-xs@2.0.1: {}
+
+ emoji-regex@10.6.0: {}
+
+ enhanced-resolve@5.26.0:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.3.3
+
+ entities@4.5.0: {}
+
+ entities@6.0.1: {}
+
+ es-module-lexer@2.3.2: {}
+
+ es-module-lexer@3.0.4: {}
+
+ esast-util-from-estree@2.0.0:
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ devlop: 1.1.0
+ estree-util-visit: 2.0.0
+ unist-util-position-from-estree: 2.0.0
+
+ esast-util-from-js@2.0.1:
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ acorn: 8.19.0
+ esast-util-from-estree: 2.0.0
+ vfile-message: 4.0.3
+
+ escalade@3.2.0: {}
+
+ escape-html@1.0.3: {}
+
+ escape-string-regexp@4.0.0: {}
+
+ escape-string-regexp@5.0.0: {}
+
+ eslint-plugin-astro@3.2.1(@typescript-eslint/parser@8.72.1(eslint@10.14.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.14.0(jiti@2.7.0)):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.14.0(jiti@2.7.0))
+ '@jridgewell/sourcemap-codec': 1.6.0
+ '@typescript-eslint/types': 8.72.1
+ astro-eslint-parser: 3.2.0
+ eslint: 10.14.0(jiti@2.7.0)
+ espree: 11.2.0
+ globals: 17.13.0
+ postcss: 8.5.32
+ postcss-selector-parser: 7.1.7
+ synckit: 0.11.13
+ optionalDependencies:
+ '@typescript-eslint/parser': 8.72.1(eslint@10.14.0(jiti@2.7.0))(typescript@6.0.3)
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-scope@9.1.2:
+ dependencies:
+ '@types/esrecurse': 4.3.1
+ '@types/estree': 1.0.9
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-visitor-keys@3.4.3: {}
+
+ eslint-visitor-keys@5.0.1: {}
+
+ eslint@10.14.0(jiti@2.7.0):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.14.0(jiti@2.7.0))
+ '@eslint-community/regexpp': 4.12.2
+ '@eslint/config-array': 0.23.5
+ '@eslint/config-helpers': 0.7.0
+ '@eslint/core': 1.2.1
+ '@eslint/plugin-kit': 0.7.3
+ '@humanfs/node': 0.16.8
+ '@humanwhocodes/module-importer': 1.0.1
+ '@humanwhocodes/retry': 0.4.3
+ '@types/estree': 1.0.9
+ ajv: 6.15.0
+ cross-spawn: 7.0.6
+ debug: 4.4.3
+ escape-string-regexp: 4.0.0
+ eslint-scope: 9.1.2
+ eslint-visitor-keys: 5.0.1
+ espree: 11.2.0
+ esquery: 1.7.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 11.1.5
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ json-stable-stringify-without-jsonify: 1.0.1
+ minimatch: 10.2.6
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ optionalDependencies:
+ jiti: 2.7.0
+ transitivePeerDependencies:
+ - supports-color
+
+ espree@11.2.0:
+ dependencies:
+ acorn: 8.19.0
+ acorn-jsx: 5.3.2(acorn@8.19.0)
+ eslint-visitor-keys: 5.0.1
+
+ esquery@1.7.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ esrecurse@4.3.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ estraverse@5.3.0: {}
+
+ estree-util-attach-comments@3.0.0:
+ dependencies:
+ '@types/estree': 1.0.9
+
+ estree-util-build-jsx@3.0.1:
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ devlop: 1.1.0
+ estree-util-is-identifier-name: 3.0.0
+ estree-walker: 3.0.3
+
+ estree-util-is-identifier-name@3.0.0: {}
+
+ estree-util-scope@1.0.1:
+ dependencies:
+ '@types/estree': 1.0.9
+ devlop: 1.1.0
+
+ estree-util-to-js@2.0.0:
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ astring: 1.9.0
+ source-map: 0.7.6
+
+ estree-util-visit@2.0.0:
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ '@types/unist': 3.0.3
+
+ estree-walker@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.9
+
+ esutils@2.0.3: {}
+
+ eventemitter3@5.0.4: {}
+
+ extend@3.0.2: {}
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-levenshtein@2.0.6: {}
+
+ fast-string-truncated-width@3.0.3: {}
+
+ fast-string-width@3.0.2:
+ dependencies:
+ fast-string-truncated-width: 3.0.3
+
+ fast-uri@3.1.9: {}
+
+ fast-wrap-ansi@0.2.2:
+ dependencies:
+ fast-string-width: 3.0.2
+
+ fast-xml-builder@1.3.1:
+ dependencies:
+ path-expression-matcher: 1.6.2
+ xml-naming: 0.3.0
+
+ fast-xml-parser@5.11.2:
+ dependencies:
+ '@nodable/entities': 3.1.0
+ fast-xml-builder: 1.3.1
+ is-unsafe: 2.0.2
+ path-expression-matcher: 1.6.2
+ strnum: 2.5.1
+ xml-naming: 0.3.0
+
+ fdir@6.5.0(picomatch@4.0.7):
+ optionalDependencies:
+ picomatch: 4.0.7
+
+ fflate@0.7.3: {}
+
+ fflate@0.7.5: {}
+
+ file-entry-cache@11.1.5:
+ dependencies:
+ flat-cache: 6.1.23
+
+ find-up@5.0.0:
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+
+ flat-cache@6.1.23:
+ dependencies:
+ cacheable: 2.5.0
+ flatted: 3.4.4
+ hookified: 1.15.1
+
+ flatted@3.4.4: {}
+
+ flattie@1.1.1: {}
+
+ fnv1a-64@0.1.2: {}
+
+ fontace@0.4.1:
+ dependencies:
+ fontkitten: 1.0.3
+
+ fontkitten@1.0.3:
+ dependencies:
+ tiny-inflate: 1.0.3
+
+ fsevents@2.3.3:
+ optional: true
+
+ get-caller-file@2.0.5: {}
+
+ get-east-asian-width@1.7.0: {}
+
+ get-tsconfig@5.0.0-beta.6:
+ dependencies:
+ resolve-pkg-maps: 1.0.0
+
+ github-slugger@2.0.0: {}
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ globals@17.13.0: {}
+
+ graceful-fs@4.2.11: {}
+
+ h3@1.15.11:
+ dependencies:
+ cookie-es: 1.2.3
+ crossws: 0.3.5
+ defu: 6.1.7
+ destr: 2.0.5
+ iron-webcrypto: 1.2.1
+ node-mock-http: 1.0.5
+ radix3: 1.1.2
+ ufo: 1.6.4
+ uncrypto: 0.1.3
+
+ harfbuzzjs@0.10.0: {}
+
+ hashery@1.5.1:
+ dependencies:
+ hookified: 1.15.1
+
+ hast-util-from-html@2.0.3:
+ dependencies:
+ '@types/hast': 3.0.5
+ devlop: 1.1.0
+ hast-util-from-parse5: 8.0.3
+ parse5: 7.3.0
+ vfile: 6.0.3
+ vfile-message: 4.0.3
+
+ hast-util-from-parse5@8.0.3:
+ dependencies:
+ '@types/hast': 3.0.5
+ '@types/unist': 3.0.3
+ devlop: 1.1.0
+ hastscript: 9.0.1
+ property-information: 7.2.0
+ vfile: 6.0.3
+ vfile-location: 5.0.3
+ web-namespaces: 2.0.1
+
+ hast-util-is-element@3.0.0:
+ dependencies:
+ '@types/hast': 3.0.5
+
+ hast-util-parse-selector@4.0.0:
+ dependencies:
+ '@types/hast': 3.0.5
+
+ hast-util-raw@9.1.0:
+ dependencies:
+ '@types/hast': 3.0.5
+ '@types/unist': 3.0.3
+ '@ungap/structured-clone': 1.4.0
+ hast-util-from-parse5: 8.0.3
+ hast-util-to-parse5: 8.0.1
+ html-void-elements: 3.0.0
+ mdast-util-to-hast: 13.2.1
+ parse5: 7.3.0
+ unist-util-position: 5.0.0
+ unist-util-visit: 5.2.0
+ vfile: 6.0.3
+ web-namespaces: 2.0.1
+ zwitch: 2.0.4
+
+ hast-util-to-estree@3.1.3:
+ dependencies:
+ '@types/estree': 1.0.9
+ '@types/estree-jsx': 1.0.5
+ '@types/hast': 3.0.5
+ comma-separated-tokens: 2.0.3
+ devlop: 1.1.0
+ estree-util-attach-comments: 3.0.0
+ estree-util-is-identifier-name: 3.0.0
+ hast-util-whitespace: 3.0.0
+ mdast-util-mdx-expression: 2.0.1
+ mdast-util-mdx-jsx: 3.2.0
+ mdast-util-mdxjs-esm: 2.0.1
+ property-information: 7.2.0
+ space-separated-tokens: 2.0.2
+ style-to-js: 1.1.21
+ unist-util-position: 5.0.0
+ zwitch: 2.0.4
+ transitivePeerDependencies:
+ - supports-color
+
+ hast-util-to-html@9.0.5:
+ dependencies:
+ '@types/hast': 3.0.5
+ '@types/unist': 3.0.3
+ ccount: 2.0.1
+ comma-separated-tokens: 2.0.3
+ hast-util-whitespace: 3.0.0
+ html-void-elements: 3.0.0
+ mdast-util-to-hast: 13.2.1
+ property-information: 7.2.0
+ space-separated-tokens: 2.0.2
+ stringify-entities: 4.0.4
+ zwitch: 2.0.4
+
+ hast-util-to-html@9.0.6:
+ dependencies:
+ '@types/hast': 3.0.5
+ '@types/unist': 3.0.3
+ ccount: 2.0.1
+ comma-separated-tokens: 2.0.3
+ hast-util-whitespace: 3.0.0
+ html-void-elements: 3.0.0
+ mdast-util-to-hast: 13.2.1
+ property-information: 7.2.0
+ space-separated-tokens: 2.0.2
+ stringify-entities: 4.0.4
+ zwitch: 2.0.4
+
+ hast-util-to-jsx-runtime@2.3.6:
+ dependencies:
+ '@types/estree': 1.0.9
+ '@types/hast': 3.0.5
+ '@types/unist': 3.0.3
+ comma-separated-tokens: 2.0.3
+ devlop: 1.1.0
+ estree-util-is-identifier-name: 3.0.0
+ hast-util-whitespace: 3.0.0
+ mdast-util-mdx-expression: 2.0.1
+ mdast-util-mdx-jsx: 3.2.0
+ mdast-util-mdxjs-esm: 2.0.1
+ property-information: 7.2.0
+ space-separated-tokens: 2.0.2
+ style-to-js: 1.1.21
+ unist-util-position: 5.0.0
+ vfile-message: 4.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ hast-util-to-parse5@8.0.1:
+ dependencies:
+ '@types/hast': 3.0.5
+ comma-separated-tokens: 2.0.3
+ devlop: 1.1.0
+ property-information: 7.2.0
+ space-separated-tokens: 2.0.2
+ web-namespaces: 2.0.1
+ zwitch: 2.0.4
+
+ hast-util-to-text@4.0.3:
+ dependencies:
+ '@types/hast': 3.0.5
+
+ hast-util-whitespace@3.0.0:
+ dependencies:
+ '@types/hast': 3.0.5
+
+ hastscript@9.0.1:
+ dependencies:
+ '@types/hast': 3.0.5
+ comma-separated-tokens: 2.0.3
+ hast-util-parse-selector: 4.0.0
+ property-information: 7.2.0
+ space-separated-tokens: 2.0.2
+
+ hex-rgb@4.3.0: {}
+
+ hookified@1.15.1: {}
+
+ hookified@2.2.0: {}
+
+ html-escaper@3.0.3: {}
+
+ html-void-elements@3.0.0: {}
+
+ http-cache-semantics@4.3.0: {}
+
+ ignore@5.3.2: {}
+
+ imurmurhash@0.1.4: {}
+
+ inline-style-parser@0.2.7: {}
+
+ iron-webcrypto@1.2.1: {}
+
+ is-alphabetical@2.0.1: {}
+
+ is-alphanumerical@2.0.1:
+ dependencies:
+ is-alphabetical: 2.0.1
+ is-decimal: 2.0.1
+
+ is-decimal@2.0.1: {}
+
+ is-docker@4.0.0: {}
+
+ is-extglob@2.1.1: {}
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-hexadecimal@2.0.1: {}
+
+ is-plain-obj@4.1.0: {}
+
+ is-unsafe@2.0.2: {}
+
+ isexe@2.0.0: {}
+
+ jiti@2.7.0: {}
+
+ js-yaml@4.3.2:
+ dependencies:
+ argparse: 2.0.1
+
+ json-schema-traverse@0.4.1: {}
+
+ json-schema-traverse@1.0.0: {}
+
+ json-stable-stringify-without-jsonify@1.0.1: {}
+
+ jsonc-parser@2.3.1: {}
+
+ jsonc-parser@3.3.1: {}
+
+ keyv@5.6.0:
+ dependencies:
+ '@keyv/serialize': 1.1.1
+
+ kleur@4.1.5: {}
+
+ levn@0.4.1:
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+
+ lightningcss-android-arm64@1.32.0:
+ optional: true
+
+ lightningcss-android-arm64@1.33.0:
+ optional: true
+
+ lightningcss-darwin-arm64@1.32.0:
+ optional: true
+
+ lightningcss-darwin-arm64@1.33.0:
+ optional: true
+
+ lightningcss-darwin-x64@1.32.0:
+ optional: true
+
+ lightningcss-darwin-x64@1.33.0:
+ optional: true
+
+ lightningcss-freebsd-x64@1.32.0:
+ optional: true
+
+ lightningcss-freebsd-x64@1.33.0:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.33.0:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.33.0:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.33.0:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.32.0:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.33.0:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.32.0:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.33.0:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.32.0:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.33.0:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.32.0:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.33.0:
+ optional: true
+
+ lightningcss@1.32.0:
+ dependencies:
+ detect-libc: 2.1.2
+ optionalDependencies:
+ lightningcss-android-arm64: 1.32.0
+ lightningcss-darwin-arm64: 1.32.0
+ lightningcss-darwin-x64: 1.32.0
+ lightningcss-freebsd-x64: 1.32.0
+ lightningcss-linux-arm-gnueabihf: 1.32.0
+ lightningcss-linux-arm64-gnu: 1.32.0
+ lightningcss-linux-arm64-musl: 1.32.0
+ lightningcss-linux-x64-gnu: 1.32.0
+ lightningcss-linux-x64-musl: 1.32.0
+ lightningcss-win32-arm64-msvc: 1.32.0
+ lightningcss-win32-x64-msvc: 1.32.0
+
+ lightningcss@1.33.0:
+ dependencies:
+ detect-libc: 2.1.2
+ 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
+
+ linebreak@1.1.0:
+ dependencies:
+ base64-js: 0.0.8
+ unicode-trie: 2.0.0
+
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
+ lodash.kebabcase@4.1.1: {}
+
+ longest-streak@3.1.0: {}
+
+ lru-cache@11.5.3: {}
+
+ magic-string@0.30.21:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.6.0
+
+ magic-string@1.4.5:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.6.0
+
+ magicast@0.5.5:
+ dependencies:
+ '@babel/parser': 7.29.11
+ '@babel/types': 7.29.11
+ source-map-js: 1.2.2
+
+ markdown-extensions@2.0.0: {}
+
+ markdown-table@3.0.4: {}
+
+ mdast-util-definitions@6.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ '@types/unist': 3.0.3
+ unist-util-visit: 5.2.0
+
+ mdast-util-find-and-replace@3.0.4:
+ dependencies:
+ '@types/mdast': 4.0.4
+ escape-string-regexp: 5.0.0
+ unist-util-is: 6.0.1
+ unist-util-visit-parents: 6.1.0
+
+ mdast-util-from-markdown@2.1.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ '@types/unist': 3.0.3
+ decode-named-character-reference: 1.3.0
+ devlop: 1.1.0
+ mdast-util-to-string: 4.0.0
+ micromark: 4.0.3
+ micromark-util-decode-numeric-character-reference: 2.0.2
+ micromark-util-decode-string: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+ unist-util-stringify-position: 4.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-autolink-literal@2.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ ccount: 2.0.1
+ devlop: 1.1.0
+ mdast-util-find-and-replace: 3.0.4
+ micromark-util-character: 2.1.1
+
+ mdast-util-gfm-footnote@2.1.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.1.0
+ mdast-util-to-markdown: 2.2.1
+ micromark-util-normalize-identifier: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-strikethrough@2.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-from-markdown: 2.1.0
+ mdast-util-to-markdown: 2.2.1
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-table@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ markdown-table: 3.0.4
+ mdast-util-from-markdown: 2.1.0
+ mdast-util-to-markdown: 2.2.1
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-task-list-item@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.1.0
+ mdast-util-to-markdown: 2.2.1
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm@3.1.0:
+ dependencies:
+ mdast-util-from-markdown: 2.1.0
+ mdast-util-gfm-autolink-literal: 2.0.1
+ mdast-util-gfm-footnote: 2.1.0
+ mdast-util-gfm-strikethrough: 2.0.1
+ mdast-util-gfm-table: 2.0.0
+ mdast-util-gfm-task-list-item: 2.0.0
+ mdast-util-to-markdown: 2.2.1
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-heading-range@2.1.5:
+ dependencies:
+ mdast-util-to-string: 1.1.0
+
+ mdast-util-mdx-expression@2.0.1:
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ '@types/hast': 3.0.5
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.1.0
+ mdast-util-to-markdown: 2.2.1
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-mdx-jsx@3.2.0:
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ '@types/hast': 3.0.5
+ '@types/mdast': 4.0.4
+ '@types/unist': 3.0.3
+ ccount: 2.0.1
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.1.0
+ mdast-util-to-markdown: 2.2.1
+ parse-entities: 4.0.2
+ stringify-entities: 4.0.4
+ unist-util-stringify-position: 4.0.0
+ vfile-message: 4.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-mdx@3.0.0:
+ dependencies:
+ mdast-util-from-markdown: 2.1.0
+ mdast-util-mdx-expression: 2.0.1
+ mdast-util-mdx-jsx: 3.2.0
+ mdast-util-mdxjs-esm: 2.0.1
+ mdast-util-to-markdown: 2.2.1
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-mdxjs-esm@2.0.1:
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ '@types/hast': 3.0.5
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.1.0
+ mdast-util-to-markdown: 2.2.1
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-phrasing@4.1.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ unist-util-is: 6.0.1
+
+ mdast-util-to-hast@13.2.1:
+ dependencies:
+ '@types/hast': 3.0.5
+ '@types/mdast': 4.0.4
+ '@ungap/structured-clone': 1.4.0
+ devlop: 1.1.0
+ micromark-util-sanitize-uri: 2.0.1
+ trim-lines: 3.0.1
+ unist-util-position: 5.0.0
+ unist-util-visit: 5.2.0
+ vfile: 6.0.3
+
+ mdast-util-to-markdown@2.2.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ '@types/unist': 3.0.3
+ longest-streak: 3.1.0
+ mdast-util-phrasing: 4.1.0
+ mdast-util-to-string: 4.0.0
+ micromark-util-character: 2.1.1
+ micromark-util-classify-character: 2.0.1
+ micromark-util-decode-string: 2.0.1
+ micromark-util-html-tag-name: 2.0.1
+ unist-util-visit: 5.2.0
+ zwitch: 2.0.4
+
+ mdast-util-to-string@1.1.0: {}
+
+ mdast-util-to-string@4.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+
+ mdast-util-toc@7.1.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ '@types/ungap__structured-clone': 1.2.0
+ '@ungap/structured-clone': 1.4.0
+ github-slugger: 2.0.0
+ mdast-util-to-string: 4.0.0
+ unist-util-is: 6.0.1
+ unist-util-visit: 5.2.0
+
+ mdn-data@2.0.28: {}
+
+ mdn-data@2.27.1: {}
+
+ micromark-core-commonmark@2.0.4:
+ dependencies:
+ decode-named-character-reference: 1.3.0
+ devlop: 1.1.0
+ micromark-factory-destination: 2.0.1
+ micromark-factory-label: 2.0.1
+ micromark-factory-space: 2.1.0
+ micromark-factory-title: 2.0.1
+ micromark-factory-whitespace: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-chunked: 2.0.1
+ micromark-util-classify-character: 2.0.1
+ micromark-util-edit-map: 1.0.0
+ micromark-util-html-tag-name: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-subtokenize: 2.1.0
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+
+ micromark-extension-gfm-autolink-literal@2.1.0:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+
+ micromark-extension-gfm-footnote@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-core-commonmark: 2.0.4
+ micromark-factory-space: 2.1.0
+ micromark-util-character: 2.1.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+
+ micromark-extension-gfm-strikethrough@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-chunked: 2.0.1
+ micromark-util-classify-character: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+
+ micromark-extension-gfm-table@2.1.2:
+ dependencies:
+ devlop: 1.1.0
+ micromark-factory-space: 2.1.0
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+
+ micromark-extension-gfm-tagfilter@2.0.0:
+ dependencies:
+ micromark-util-types: 2.0.3
+
+ micromark-extension-gfm-task-list-item@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-factory-space: 2.1.0
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+
+ micromark-extension-gfm@3.0.0:
+ dependencies:
+ micromark-extension-gfm-autolink-literal: 2.1.0
+ micromark-extension-gfm-footnote: 2.1.0
+ micromark-extension-gfm-strikethrough: 2.1.0
+ micromark-extension-gfm-table: 2.1.2
+ micromark-extension-gfm-tagfilter: 2.0.0
+ micromark-extension-gfm-task-list-item: 2.1.0
+ micromark-util-combine-extensions: 2.0.1
+ micromark-util-types: 2.0.3
+
+ micromark-extension-mdx-expression@3.0.1:
+ dependencies:
+ '@types/estree': 1.0.9
+ devlop: 1.1.0
+ micromark-factory-mdx-expression: 2.0.3
+ micromark-factory-space: 2.1.0
+ micromark-util-character: 2.1.1
+ micromark-util-events-to-acorn: 2.0.3
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+
+ micromark-extension-mdx-jsx@3.0.2:
+ dependencies:
+ '@types/estree': 1.0.9
+ devlop: 1.1.0
+ estree-util-is-identifier-name: 3.0.0
+ micromark-factory-mdx-expression: 2.0.3
+ micromark-factory-space: 2.1.0
+ micromark-util-character: 2.1.1
+ micromark-util-events-to-acorn: 2.0.3
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+ vfile-message: 4.0.3
+
+ micromark-extension-mdx-md@2.0.0:
+ dependencies:
+ micromark-util-types: 2.0.3
+
+ micromark-extension-mdxjs-esm@3.0.0:
+ dependencies:
+ '@types/estree': 1.0.9
+ devlop: 1.1.0
+ micromark-core-commonmark: 2.0.4
+ micromark-util-character: 2.1.1
+ micromark-util-events-to-acorn: 2.0.3
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+ unist-util-position-from-estree: 2.0.0
+ vfile-message: 4.0.3
+
+ micromark-extension-mdxjs@3.0.0:
+ dependencies:
+ acorn: 8.19.0
+ acorn-jsx: 5.3.2(acorn@8.19.0)
+ micromark-extension-mdx-expression: 3.0.1
+ micromark-extension-mdx-jsx: 3.0.2
+ micromark-extension-mdx-md: 2.0.0
+ micromark-extension-mdxjs-esm: 3.0.0
+ micromark-util-combine-extensions: 2.0.1
+ micromark-util-types: 2.0.3
+
+ micromark-factory-destination@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+
+ micromark-factory-label@2.0.1:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+
+ micromark-factory-mdx-expression@2.0.3:
+ dependencies:
+ '@types/estree': 1.0.9
+ devlop: 1.1.0
+ micromark-factory-space: 2.1.0
+ micromark-util-character: 2.1.1
+ micromark-util-events-to-acorn: 2.0.3
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+ unist-util-position-from-estree: 2.0.0
+ vfile-message: 4.0.3
+
+ micromark-factory-space@2.1.0:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-types: 2.0.3
+
+ micromark-factory-title@2.0.1:
+ dependencies:
+ micromark-factory-space: 2.1.0
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+
+ micromark-factory-whitespace@2.0.1:
+ dependencies:
+ micromark-factory-space: 2.1.0
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+
+ micromark-util-character@2.1.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+
+ micromark-util-chunked@2.0.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-classify-character@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+
+ micromark-util-combine-extensions@2.0.1:
+ dependencies:
+ micromark-util-chunked: 2.0.1
+ micromark-util-types: 2.0.3
+
+ micromark-util-decode-numeric-character-reference@2.0.2:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-decode-string@2.0.1:
+ dependencies:
+ decode-named-character-reference: 1.3.0
+ micromark-util-character: 2.1.1
+ micromark-util-decode-numeric-character-reference: 2.0.2
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-edit-map@1.0.0:
+ dependencies:
+ micromark-util-types: 2.0.3
+
+ micromark-util-encode@2.0.1: {}
+
+ micromark-util-events-to-acorn@2.0.3:
+ dependencies:
+ '@types/estree': 1.0.9
+ '@types/unist': 3.0.3
+ devlop: 1.1.0
+ estree-util-visit: 2.0.0
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+ vfile-message: 4.0.3
+
+ micromark-util-html-tag-name@2.0.1: {}
+
+ micromark-util-normalize-identifier@2.0.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-resolve-all@2.0.1:
+ dependencies:
+ micromark-util-types: 2.0.3
+
+ micromark-util-sanitize-uri@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-encode: 2.0.1
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-subtokenize@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-chunked: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+
+ micromark-util-symbol@2.0.1: {}
+
+ micromark-util-types@2.0.3: {}
+
+ micromark@4.0.3:
+ dependencies:
+ '@types/debug': 4.1.13
+ debug: 4.4.3
+ decode-named-character-reference: 1.3.0
+ devlop: 1.1.0
+ micromark-core-commonmark: 2.0.4
+ micromark-factory-space: 2.1.0
+ micromark-util-character: 2.1.1
+ micromark-util-chunked: 2.0.1
+ micromark-util-combine-extensions: 2.0.1
+ micromark-util-decode-numeric-character-reference: 2.0.2
+ micromark-util-edit-map: 1.0.0
+ micromark-util-encode: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-subtokenize: 2.1.0
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ minimatch@10.2.6:
+ dependencies:
+ brace-expansion: 5.0.12
+
+ mrmime@2.0.1: {}
+
+ ms@2.1.3: {}
+
+ muggle-string@0.4.1: {}
+
+ nanoid@3.3.20: {}
+
+ natural-compare@1.4.0: {}
+
+ neotraverse@1.0.1: {}
+
+ nlcst-to-string@4.0.0:
+ dependencies:
+ '@types/nlcst': 2.0.3
+
+ node-fetch-native@1.6.7: {}
+
+ node-mock-http@1.0.5: {}
+
+ normalize-path@3.0.0: {}
+
+ nth-check@2.1.1:
+ dependencies:
+ boolbase: 1.0.0
+
+ obug@3.0.0: {}
+
+ ofetch@1.5.1:
+ dependencies:
+ destr: 2.0.5
+ node-fetch-native: 1.6.7
+ ufo: 1.6.4
+
+ oniguruma-parser@0.12.2: {}
+
+ oniguruma-to-es@4.3.6:
+ dependencies:
+ oniguruma-parser: 0.12.2
+ regex: 6.1.0
+ regex-recursion: 6.0.2
+
+ optionator@0.9.4:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.5
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-limit@7.3.3:
+ dependencies:
+ yocto-queue: 1.2.2
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
+ p-queue@9.3.5:
+ dependencies:
+ eventemitter3: 5.0.4
+ p-timeout: 7.0.2
+
+ p-timeout@7.0.2: {}
+
+ package-manager-detector@1.9.0: {}
+
+ pagefind@1.6.0:
+ optionalDependencies:
+ '@pagefind/darwin-arm64': 1.6.0
+ '@pagefind/darwin-x64': 1.6.0
+ '@pagefind/freebsd-x64': 1.6.0
+ '@pagefind/linux-arm64': 1.6.0
+ '@pagefind/linux-x64': 1.6.0
+ '@pagefind/windows-arm64': 1.6.0
+ '@pagefind/windows-x64': 1.6.0
+
+ pako@0.2.9: {}
+
+ parse-css-color@0.2.1:
+ dependencies:
+ color-name: 1.1.4
+ hex-rgb: 4.3.0
+
+ parse-entities@4.0.2:
+ dependencies:
+ '@types/unist': 2.0.11
+ character-entities-legacy: 3.0.0
+ character-reference-invalid: 2.0.1
+ decode-named-character-reference: 1.3.0
+ is-alphanumerical: 2.0.1
+ is-decimal: 2.0.1
+ is-hexadecimal: 2.0.1
+
+ parse-latin@7.0.0:
+ dependencies:
+ '@types/nlcst': 2.0.3
+ '@types/unist': 3.0.3
+ nlcst-to-string: 4.0.0
+ unist-util-modify-children: 4.0.0
+ unist-util-visit-children: 3.0.0
+ vfile: 6.0.3
+
+ parse5@7.3.0:
+ dependencies:
+ entities: 6.0.1
+
+ path-browserify@1.0.1: {}
+
+ path-exists@4.0.0: {}
+
+ path-expression-matcher@1.6.2: {}
+
+ path-key@3.1.1: {}
+
+ piccolore@0.1.3: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.2: {}
+
+ picomatch@4.0.7: {}
+
+ postcss-selector-parser@6.0.10:
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ postcss-selector-parser@7.1.7:
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ postcss-value-parser@4.2.0: {}
+
+ postcss@8.5.32:
+ dependencies:
+ nanoid: 3.3.20
+ picocolors: 1.1.1
+ source-map-js: 1.2.2
+
+ prelude-ls@1.2.1: {}
+
+ prettier-plugin-astro@0.14.1:
+ dependencies:
+ '@astrojs/compiler': 2.13.1
+ prettier: 3.9.10
+ sass-formatter: 0.7.9
+
+ prettier-plugin-tailwindcss@0.8.1(prettier-plugin-astro@0.14.1)(prettier@3.9.10):
+ dependencies:
+ prettier: 3.9.10
+ optionalDependencies:
+ prettier-plugin-astro: 0.14.1
+
+ prettier@3.9.10: {}
+
+ prismjs@1.30.0: {}
+
+ process-ancestry@0.1.0: {}
+
+ property-information@7.2.0: {}
+
+ punycode@2.3.1: {}
+
+ qified@0.10.1:
+ dependencies:
+ hookified: 2.2.0
+
+ radix3@1.1.2: {}
+
+ readdirp@5.1.1: {}
+
+ recma-build-jsx@1.0.0:
+ dependencies:
+ '@types/estree': 1.0.9
+ estree-util-build-jsx: 3.0.1
+ vfile: 6.0.3
+
+ recma-jsx@1.0.1(acorn@8.19.0):
+ dependencies:
+ acorn: 8.19.0
+ acorn-jsx: 5.3.2(acorn@8.19.0)
+ estree-util-to-js: 2.0.0
+ recma-parse: 1.0.0
+ recma-stringify: 1.0.0
+ unified: 11.0.5
+
+ recma-parse@1.0.0:
+ dependencies:
+ '@types/estree': 1.0.9
+ esast-util-from-js: 2.0.1
+ unified: 11.0.5
+ vfile: 6.0.3
+
+ recma-stringify@1.0.0:
+ dependencies:
+ '@types/estree': 1.0.9
+ estree-util-to-js: 2.0.0
+ unified: 11.0.5
+ vfile: 6.0.3
+
+ regex-recursion@6.0.2:
+ dependencies:
+ regex-utilities: 2.3.0
+
+ regex-utilities@2.3.0: {}
+
+ regex@6.1.0:
+ dependencies:
+ regex-utilities: 2.3.0
+
+ rehype-callouts@2.2.0:
+ dependencies:
+ '@types/hast': 3.0.5
+ hast-util-from-html: 2.0.3
+ hast-util-is-element: 3.0.0
+ hastscript: 9.0.1
+ unist-util-visit: 5.2.0
+
+ rehype-raw@7.0.0:
+ dependencies:
+ '@types/hast': 3.0.5
+ hast-util-raw: 9.1.0
+ vfile: 6.0.3
+
+ rehype-recma@1.0.0:
+ dependencies:
+ '@types/estree': 1.0.9
+ '@types/hast': 3.0.5
+ hast-util-to-estree: 3.1.3
+ transitivePeerDependencies:
+ - supports-color
+
+ rehype-stringify@10.0.1:
+ dependencies:
+ '@types/hast': 3.0.5
+ hast-util-to-html: 9.0.5
+ unified: 11.0.5
+
+ remark-collapse@0.1.2:
+ dependencies:
+ mdast-util-heading-range: 2.1.5
+ mdast-util-to-string: 1.1.0
+
+ remark-gfm@4.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-gfm: 3.1.0
+ micromark-extension-gfm: 3.0.0
+ remark-parse: 11.0.0
+ remark-stringify: 11.0.0
+ unified: 11.0.5
+ transitivePeerDependencies:
+ - supports-color
+
+ remark-mdx@3.1.1:
+ dependencies:
+ mdast-util-mdx: 3.0.0
+ micromark-extension-mdxjs: 3.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ remark-parse@11.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-from-markdown: 2.1.0
+ micromark-util-types: 2.0.3
+ unified: 11.0.5
+ transitivePeerDependencies:
+ - supports-color
+
+ remark-rehype@11.1.2:
+ dependencies:
+ '@types/hast': 3.0.5
+ '@types/mdast': 4.0.4
+ mdast-util-to-hast: 13.2.1
+ unified: 11.0.5
+ vfile: 6.0.3
+
+ remark-smartypants@3.0.3:
+ dependencies:
+ retext: 9.0.0
+ retext-smartypants: 6.2.0
+ unified: 11.0.5
+ unist-util-visit: 5.2.0
+
+ remark-stringify@11.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-to-markdown: 2.2.1
+ unified: 11.0.5
+
+ remark-toc@9.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-toc: 7.1.0
+
+ request-light@0.5.8: {}
+
+ request-light@0.7.0: {}
+
+ require-from-string@2.0.2: {}
+
+ resolve-pkg-maps@1.0.0: {}
+
+ retext-latin@4.0.0:
+ dependencies:
+ '@types/nlcst': 2.0.3
+ parse-latin: 7.0.0
+ unified: 11.0.5
+
+ retext-smartypants@6.2.0:
+ dependencies:
+ '@types/nlcst': 2.0.3
+ nlcst-to-string: 4.0.0
+ unist-util-visit: 5.2.0
+
+ retext-stringify@4.0.0:
+ dependencies:
+ '@types/nlcst': 2.0.3
+ nlcst-to-string: 4.0.0
+ unified: 11.0.5
+
+ retext@9.0.0:
+ dependencies:
+ '@types/nlcst': 2.0.3
+ retext-latin: 4.0.0
+ retext-stringify: 4.0.0
+ unified: 11.0.5
+
+ rolldown@1.2.15:
+ dependencies:
+ '@oxc-project/types': 0.155.0
+ '@rolldown/pluginutils': 1.0.1
+ 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
+
+ s.color@0.0.15: {}
+
+ sass-formatter@0.7.9:
+ dependencies:
+ suf-log: 2.5.3
+
+ satori@0.63.2:
+ dependencies:
+ '@shuding/opentype.js': 1.4.0-beta.0
+ css-background-parser: 0.1.0
+ css-box-shadow: 1.0.0-3
+ css-gradient-parser: 0.0.17
+ css-to-react-native: 3.2.0
+ emoji-regex-xs: 2.0.1
+ escape-html: 1.0.3
+ fflate: 0.7.3
+ harfbuzzjs: 0.10.0
+ linebreak: 1.1.0
+ parse-css-color: 0.2.1
+ postcss-value-parser: 4.2.0
+
+ satteri@0.10.5:
+ 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
+
+ sax@1.6.1: {}
+
+ semver@7.8.5: {}
+
+ sharp@0.35.5(@types/node@24.19.2):
+ dependencies:
+ '@img/colour': 1.1.0
+ detect-libc: 2.1.2
+ semver: 7.8.5
+ 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
+ '@types/node': 24.19.2
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ shiki@4.5.0:
+ 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.1.0
+ '@types/hast': 3.0.5
+
+ sisteransi@1.0.5: {}
+
+ sitemap@9.0.1:
+ dependencies:
+ '@types/node': 24.19.2
+ '@types/sax': 1.2.7
+ arg: 5.0.2
+ sax: 1.6.1
+
+ slugify@1.6.9: {}
+
+ smol-toml@1.9.1: {}
+
+ source-map-js@1.2.2: {}
+
+ source-map@0.7.6: {}
+
+ source-map@0.8.0: {}
+
+ space-separated-tokens@2.0.2: {}
+
+ std-env@4.3.0: {}
+
+ string-width@7.2.0:
+ dependencies:
+ emoji-regex: 10.6.0
+ get-east-asian-width: 1.7.0
+ strip-ansi: 7.2.0
+
+ string-width@8.3.0:
+ dependencies:
+ get-east-asian-width: 1.7.0
+ strip-ansi: 7.2.0
+
+ string.prototype.codepointat@0.2.1: {}
+
+ stringify-entities@4.0.4:
+ dependencies:
+ character-entities-html4: 2.1.0
+ character-entities-legacy: 3.0.0
+
+ strip-ansi@7.2.0:
+ dependencies:
+ ansi-regex: 6.4.0
+
+ strnum@2.5.1:
+ dependencies:
+ anynum: 1.0.1
+
+ style-to-js@1.1.21:
+ dependencies:
+ style-to-object: 1.0.14
+
+ style-to-object@1.0.14:
+ dependencies:
+ inline-style-parser: 0.2.7
+
+ suf-log@2.5.3:
+ dependencies:
+ s.color: 0.0.15
+
+ svgo@4.1.0:
+ dependencies:
+ commander: 11.1.0
+ css-select: 6.0.0
+ css-tree: 3.2.1
+ css-what: 7.0.0
+ csso: 5.0.5
+ picocolors: 1.1.1
+ sax: 1.6.1
+
+ synckit@0.11.13:
+ dependencies:
+ '@pkgr/core': 0.3.6
+
+ tailwindcss@4.3.3: {}
+
+ tapable@2.3.3: {}
+
+ tiny-inflate@1.0.3: {}
+
+ tinyclip@1.0.3: {}
+
+ tinyexec@1.3.1: {}
+
+ tinyglobby@0.2.17:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.7)
+ picomatch: 4.0.7
+
+ tinyps@0.5.1: {}
+
+ trim-lines@3.0.1: {}
+
+ trough@2.2.0: {}
+
+ ts-api-utils@2.5.0(typescript@6.0.3):
+ dependencies:
+ typescript: 6.0.3
+
+ tslib@2.8.1: {}
+
+ type-check@0.4.0:
+ dependencies:
+ prelude-ls: 1.2.1
+
+ typesafe-path@0.2.2: {}
+
+ typescript-auto-import-cache@0.3.6:
+ dependencies:
+ semver: 7.8.5
+
+ typescript@6.0.3: {}
+
+ ufo@1.6.4: {}
+
+ ultrahtml@1.7.0: {}
+
+ uncrypto@0.1.3: {}
+
+ undici-types@7.24.6: {}
+
+ undici@8.12.3: {}
+
+ unicode-trie@2.0.0:
+ dependencies:
+ pako: 0.2.9
+ tiny-inflate: 1.0.3
+
+ unified@11.0.5:
+ dependencies:
+ '@types/unist': 3.0.3
+ bail: 2.0.2
+ devlop: 1.1.0
+ extend: 3.0.2
+ is-plain-obj: 4.1.0
+ trough: 2.2.0
+ vfile: 6.0.3
+
+ unifont@1.0.3:
+ dependencies:
+ css-tree: 3.2.1
+ fnv1a-64: 0.1.2
+ std-env: 4.3.0
+ undici: 8.12.3
+
+ unist-util-is@6.0.1:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-modify-children@4.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+ array-iterate: 2.0.1
+
+ unist-util-position-from-estree@2.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-position@5.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-remove-position@5.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-visit: 5.2.0
+
+ unist-util-stringify-position@4.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-visit-children@3.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-visit-parents@6.1.0:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.1
+
+ unist-util-visit@5.2.0:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.1
+ unist-util-visit-parents: 6.1.0
+
+ unstorage@1.17.6:
+ 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
+
+ uri-js@4.4.1:
+ dependencies:
+ punycode: 2.3.1
+
+ util-deprecate@1.0.2: {}
+
+ verkit@0.5.0: {}
+
+ vfile-location@5.0.3:
+ dependencies:
+ '@types/unist': 3.0.3
+ vfile: 6.0.3
+
+ vfile-message@4.0.3:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-stringify-position: 4.0.0
+
+ vfile@6.0.3:
+ dependencies:
+ '@types/unist': 3.0.3
+ vfile-message: 4.0.3
+
+ vite@8.3.5(@types/node@24.19.2)(jiti@2.7.0)(yaml@2.9.1):
+ dependencies:
+ lightningcss: 1.33.0
+ picomatch: 4.0.7
+ postcss: 8.5.32
+ rolldown: 1.2.15
+ tinyglobby: 0.2.17
+ optionalDependencies:
+ '@types/node': 24.19.2
+ fsevents: 2.3.3
+ jiti: 2.7.0
+ yaml: 2.9.1
+
+ vitefu@1.1.3(vite@8.3.5(@types/node@24.19.2)(jiti@2.7.0)(yaml@2.9.1)):
+ optionalDependencies:
+ vite: 8.3.5(@types/node@24.19.2)(jiti@2.7.0)(yaml@2.9.1)
+
+ volar-service-css@0.0.71(@volar/language-service@2.4.30(typescript@6.0.3)):
+ dependencies:
+ vscode-css-languageservice: 6.3.10
+ vscode-languageserver-textdocument: 1.0.16
+ vscode-uri: 3.2.0
+ optionalDependencies:
+ '@volar/language-service': 2.4.30(typescript@6.0.3)
+
+ volar-service-emmet@0.0.71(@volar/language-service@2.4.30(typescript@6.0.3)):
+ dependencies:
+ '@emmetio/css-parser': 0.4.1
+ '@emmetio/html-matcher': 1.3.0
+ '@vscode/emmet-helper': 2.11.0
+ vscode-uri: 3.2.0
+ optionalDependencies:
+ '@volar/language-service': 2.4.30(typescript@6.0.3)
+
+ volar-service-html@0.0.71(@volar/language-service@2.4.30(typescript@6.0.3)):
+ dependencies:
+ vscode-html-languageservice: 5.6.2
+ vscode-languageserver-textdocument: 1.0.16
+ vscode-uri: 3.2.0
+ optionalDependencies:
+ '@volar/language-service': 2.4.30(typescript@6.0.3)
+
+ volar-service-prettier@0.0.71(@volar/language-service@2.4.30(typescript@6.0.3))(prettier@3.9.10):
+ dependencies:
+ vscode-uri: 3.2.0
+ optionalDependencies:
+ '@volar/language-service': 2.4.30(typescript@6.0.3)
+ prettier: 3.9.10
+
+ volar-service-typescript-twoslash-queries@0.0.71(@volar/language-service@2.4.30(typescript@6.0.3)):
+ dependencies:
+ vscode-uri: 3.2.0
+ optionalDependencies:
+ '@volar/language-service': 2.4.30(typescript@6.0.3)
+
+ volar-service-typescript@0.0.71(@volar/language-service@2.4.30(typescript@6.0.3)):
+ dependencies:
+ path-browserify: 1.0.1
+ semver: 7.8.5
+ typescript-auto-import-cache: 0.3.6
+ vscode-languageserver-textdocument: 1.0.16
+ vscode-nls: 5.2.0
+ vscode-uri: 3.2.0
+ optionalDependencies:
+ '@volar/language-service': 2.4.30(typescript@6.0.3)
+
+ volar-service-yaml@0.0.71(@volar/language-service@2.4.30(typescript@6.0.3)):
+ dependencies:
+ vscode-uri: 3.2.0
+ yaml-language-server: 1.23.0
+ optionalDependencies:
+ '@volar/language-service': 2.4.30(typescript@6.0.3)
+
+ vscode-css-languageservice@6.3.10:
+ dependencies:
+ '@vscode/l10n': 0.0.18
+ vscode-languageserver-textdocument: 1.0.16
+ vscode-languageserver-types: 3.17.5
+ vscode-uri: 3.2.0
+
+ vscode-html-languageservice@5.6.2:
+ dependencies:
+ '@vscode/l10n': 0.0.18
+ vscode-languageserver-textdocument: 1.0.16
+ vscode-languageserver-types: 3.18.5
+ vscode-uri: 3.2.0
+
+ vscode-json-languageservice@4.1.8:
+ dependencies:
+ jsonc-parser: 3.3.1
+ vscode-languageserver-textdocument: 1.0.16
+ vscode-languageserver-types: 3.18.5
+ vscode-nls: 5.2.0
+ vscode-uri: 3.2.0
+
+ vscode-jsonrpc@10.0.0: {}
+
+ vscode-jsonrpc@8.2.0: {}
+
+ vscode-languageserver-protocol@3.17.5:
+ dependencies:
+ vscode-jsonrpc: 8.2.0
+ vscode-languageserver-types: 3.17.5
+
+ vscode-languageserver-protocol@3.18.5:
+ dependencies:
+ vscode-jsonrpc: 10.0.0
+ vscode-languageserver-types: 3.18.5
+
+ vscode-languageserver-textdocument@1.0.16: {}
+
+ vscode-languageserver-types@3.17.5: {}
+
+ vscode-languageserver-types@3.18.5: {}
+
+ vscode-languageserver@9.0.1:
+ dependencies:
+ vscode-languageserver-protocol: 3.17.5
+
+ vscode-nls@5.2.0: {}
+
+ vscode-uri@3.2.0: {}
+
+ web-namespaces@2.0.1: {}
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ word-wrap@1.2.5: {}
+
+ wrap-ansi@9.0.2:
+ dependencies:
+ ansi-styles: 6.2.3
+ string-width: 7.2.0
+ strip-ansi: 7.2.0
+
+ xml-naming@0.3.0: {}
+
+ xxhash-wasm@1.1.0: {}
+
+ y18n@5.0.8: {}
+
+ yaml-language-server@1.23.0:
+ dependencies:
+ '@vscode/l10n': 0.0.18
+ ajv: 8.20.0
+ ajv-draft-04: 1.0.0(ajv@8.20.0)
+ ajv-i18n: 4.2.0(ajv@8.20.0)
+ prettier: 3.9.10
+ request-light: 0.5.8
+ vscode-json-languageservice: 4.1.8
+ vscode-languageserver: 9.0.1
+ vscode-languageserver-textdocument: 1.0.16
+ vscode-languageserver-types: 3.18.5
+ vscode-uri: 3.2.0
+ yaml: 2.8.3
+
+ yaml@2.8.3: {}
+
+ yaml@2.9.1: {}
+
+ yargs-parser@22.0.0: {}
+
+ yargs@18.2.0:
+ dependencies:
+ cliui: 9.0.1
+ escalade: 3.2.0
+ get-caller-file: 2.0.5
+ string-width: 8.3.0
+ y18n: 5.0.8
+ yargs-parser: 22.0.0
+
+ yocto-queue@0.1.0: {}
+
+ yocto-queue@1.2.2: {}
+
+ zod@4.6.7: {}
+
+ zwitch@2.0.4: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
new file mode 100644
index 0000000..2eeaa78
--- /dev/null
+++ b/pnpm-workspace.yaml
@@ -0,0 +1,5 @@
+# pnpm < 10.5 refuses a workspace file without `packages`
+packages: ["."]
+allowBuilds:
+ esbuild: true
+ sharp: true
diff --git a/CNAME b/public/CNAME
similarity index 100%
rename from CNAME
rename to public/CNAME
diff --git a/public/assets/css/spa-decoder.css b/public/assets/css/spa-decoder.css
new file mode 100644
index 0000000..c4c14e7
--- /dev/null
+++ b/public/assets/css/spa-decoder.css
@@ -0,0 +1,102 @@
+/* Interactive decoder (public/assets/js/spa-decoder.js). Colours come from the theme. */
+
+.decoder {
+ margin: 0 0 1.5rem;
+ padding: 1rem;
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ background: var(--muted);
+}
+
+.decoder-samples {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 0.5rem;
+ margin-bottom: 0.75rem;
+}
+
+.decoder-label,
+.decoder-unit {
+ color: var(--muted-foreground);
+ font-size: 0.9rem;
+}
+
+.decoder button,
+.decoder select {
+ padding: 0.3rem 0.65rem;
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ background: var(--background);
+ color: var(--foreground);
+ font: inherit;
+ font-size: 0.85rem;
+}
+
+.decoder button {
+ cursor: pointer;
+}
+
+.decoder button:hover {
+ border-color: var(--accent);
+}
+
+.decoder-input {
+ display: block;
+ width: 100%;
+ margin-bottom: 0.75rem;
+ padding: 0.6rem;
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ background: var(--background);
+ color: var(--foreground);
+ font-size: 0.82rem;
+ line-height: 1.5;
+ resize: vertical;
+}
+
+.decoder-output {
+ margin-top: 0.75rem;
+}
+
+.decoder-frame {
+ margin-top: 0.75rem;
+ padding: 0.75rem;
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ background: var(--background);
+}
+
+.decoder-frame-title {
+ margin: 0;
+ font-weight: 600;
+}
+
+.decoder-frame dl {
+ display: grid;
+ grid-template-columns: 8.5rem 1fr;
+ gap: 0.2rem 1rem;
+ margin: 0.5rem 0 0;
+ font-size: 0.92rem;
+}
+
+.decoder-frame dt {
+ color: var(--muted-foreground);
+}
+
+.decoder-frame dd {
+ margin: 0;
+ font-size: 0.88em;
+ overflow-wrap: anywhere;
+}
+
+.decoder-empty {
+ margin: 0;
+ color: var(--muted-foreground);
+}
+
+@media (max-width: 32rem) {
+ .decoder-frame dl {
+ grid-template-columns: 1fr;
+ }
+}
diff --git a/assets/js/spa-decoder.js b/public/assets/js/spa-decoder.js
similarity index 96%
rename from assets/js/spa-decoder.js
rename to public/assets/js/spa-decoder.js
index 6339c18..1552091 100644
--- a/assets/js/spa-decoder.js
+++ b/public/assets/js/spa-decoder.js
@@ -328,8 +328,15 @@
render();
}
- document.addEventListener('DOMContentLoaded', function () {
+ function init() {
var container = document.getElementById('spa-decoder');
- if (container) mount(container);
- });
+ if (!container || container.dataset.mounted) return;
+ container.dataset.mounted = 'true';
+ mount(container);
+ }
+
+ // astro:page-load also fires after client-side navigation, when this script does not re-run.
+ document.addEventListener('DOMContentLoaded', init);
+ document.addEventListener('astro:page-load', init);
+ if (document.readyState !== 'loading') init();
})(this);
diff --git a/public/automotive/can/bmw/f30/2023/08/15/CAN-Bus-Hacking.html b/public/automotive/can/bmw/f30/2023/08/15/CAN-Bus-Hacking.html
new file mode 100644
index 0000000..b693432
--- /dev/null
+++ b/public/automotive/can/bmw/f30/2023/08/15/CAN-Bus-Hacking.html
@@ -0,0 +1,13 @@
+
+
+
+
+ CAN Bus Hacking
+
+
+
+
+
+ This post has moved to /posts/can-bus-hacking/ .
+
+
diff --git a/public/favicon.svg b/public/favicon.svg
new file mode 100644
index 0000000..d980dab
--- /dev/null
+++ b/public/favicon.svg
@@ -0,0 +1,4 @@
+
+
+ SL
+
diff --git a/src/assets/icons/IconArchive.svg b/src/assets/icons/IconArchive.svg
new file mode 100644
index 0000000..e7e53af
--- /dev/null
+++ b/src/assets/icons/IconArchive.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconArrowLeft.svg b/src/assets/icons/IconArrowLeft.svg
new file mode 100644
index 0000000..6bb4b11
--- /dev/null
+++ b/src/assets/icons/IconArrowLeft.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconArrowNarrowUp.svg b/src/assets/icons/IconArrowNarrowUp.svg
new file mode 100644
index 0000000..ce54fda
--- /dev/null
+++ b/src/assets/icons/IconArrowNarrowUp.svg
@@ -0,0 +1 @@
+
diff --git a/src/assets/icons/IconArrowRight.svg b/src/assets/icons/IconArrowRight.svg
new file mode 100644
index 0000000..a0f23ae
--- /dev/null
+++ b/src/assets/icons/IconArrowRight.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconCalendar.svg b/src/assets/icons/IconCalendar.svg
new file mode 100644
index 0000000..5f0be65
--- /dev/null
+++ b/src/assets/icons/IconCalendar.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconChevronLeft.svg b/src/assets/icons/IconChevronLeft.svg
new file mode 100644
index 0000000..e54e8fb
--- /dev/null
+++ b/src/assets/icons/IconChevronLeft.svg
@@ -0,0 +1 @@
+
diff --git a/src/assets/icons/IconEdit.svg b/src/assets/icons/IconEdit.svg
new file mode 100644
index 0000000..f351386
--- /dev/null
+++ b/src/assets/icons/IconEdit.svg
@@ -0,0 +1 @@
+
diff --git a/src/assets/icons/IconHash.svg b/src/assets/icons/IconHash.svg
new file mode 100644
index 0000000..6bb6496
--- /dev/null
+++ b/src/assets/icons/IconHash.svg
@@ -0,0 +1 @@
+
diff --git a/src/assets/icons/IconListTree.svg b/src/assets/icons/IconListTree.svg
new file mode 100644
index 0000000..3f92046
--- /dev/null
+++ b/src/assets/icons/IconListTree.svg
@@ -0,0 +1 @@
+
diff --git a/src/assets/icons/IconMenuDeep.svg b/src/assets/icons/IconMenuDeep.svg
new file mode 100644
index 0000000..5d951a7
--- /dev/null
+++ b/src/assets/icons/IconMenuDeep.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconMoon.svg b/src/assets/icons/IconMoon.svg
new file mode 100644
index 0000000..ec6ba71
--- /dev/null
+++ b/src/assets/icons/IconMoon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconRss.svg b/src/assets/icons/IconRss.svg
new file mode 100644
index 0000000..7751556
--- /dev/null
+++ b/src/assets/icons/IconRss.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconSearch.svg b/src/assets/icons/IconSearch.svg
new file mode 100644
index 0000000..0f6d821
--- /dev/null
+++ b/src/assets/icons/IconSearch.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconSunHigh.svg b/src/assets/icons/IconSunHigh.svg
new file mode 100644
index 0000000..9902622
--- /dev/null
+++ b/src/assets/icons/IconSunHigh.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/IconUnderline.svg b/src/assets/icons/IconUnderline.svg
new file mode 100644
index 0000000..383f8a4
--- /dev/null
+++ b/src/assets/icons/IconUnderline.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/icons/IconX.svg b/src/assets/icons/IconX.svg
new file mode 100644
index 0000000..79f489b
--- /dev/null
+++ b/src/assets/icons/IconX.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/socials/facebook.svg b/src/assets/icons/socials/facebook.svg
new file mode 100644
index 0000000..47ad743
--- /dev/null
+++ b/src/assets/icons/socials/facebook.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/socials/github.svg b/src/assets/icons/socials/github.svg
new file mode 100644
index 0000000..ad12afd
--- /dev/null
+++ b/src/assets/icons/socials/github.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/socials/linkedin.svg b/src/assets/icons/socials/linkedin.svg
new file mode 100644
index 0000000..a98e151
--- /dev/null
+++ b/src/assets/icons/socials/linkedin.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/socials/mail.svg b/src/assets/icons/socials/mail.svg
new file mode 100644
index 0000000..cecc719
--- /dev/null
+++ b/src/assets/icons/socials/mail.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/socials/pinterest.svg b/src/assets/icons/socials/pinterest.svg
new file mode 100644
index 0000000..a398d34
--- /dev/null
+++ b/src/assets/icons/socials/pinterest.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/socials/telegram.svg b/src/assets/icons/socials/telegram.svg
new file mode 100644
index 0000000..113242f
--- /dev/null
+++ b/src/assets/icons/socials/telegram.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/socials/whatsapp.svg b/src/assets/icons/socials/whatsapp.svg
new file mode 100644
index 0000000..2e2de46
--- /dev/null
+++ b/src/assets/icons/socials/whatsapp.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/socials/x.svg b/src/assets/icons/socials/x.svg
new file mode 100644
index 0000000..0feee2b
--- /dev/null
+++ b/src/assets/icons/socials/x.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/components/Breadcrumb.astro b/src/components/Breadcrumb.astro
new file mode 100644
index 0000000..633f7c9
--- /dev/null
+++ b/src/components/Breadcrumb.astro
@@ -0,0 +1,98 @@
+---
+import { getRelativeLocaleUrl } from "astro:i18n";
+import { stripBase, stripLocale } from "@/utils/withBase";
+import { useTranslations } from "@/i18n";
+import config from "@/config";
+
+const locale = Astro.currentLocale ?? config.site.lang;
+const t = useTranslations(locale);
+
+// Strip base prefix and trailing slash, then locale prefix so segments
+// reflect the logical page path only.
+const pathWithoutBase = stripBase(Astro.url.pathname).replace(/\/+$/, "");
+const currentUrlPath = stripLocale(pathWithoutBase, locale);
+
+// Get url array from path
+// eg: /tags/tailwindcss => ['tags', 'tailwindcss']
+const breadcrumbList = currentUrlPath.split("/").slice(1).filter(Boolean);
+
+const decodeSegment = (value: string) => {
+ try {
+ return decodeURIComponent(value);
+ } catch {
+ return value;
+ }
+};
+
+const navLabels: Record = {
+ posts: t.nav.posts,
+ tags: t.nav.tags,
+ about: t.nav.about,
+ archives: t.nav.archives,
+ search: t.nav.search,
+};
+
+// if breadcrumb is Home > Posts > [page]
+// replace Posts with localized "Posts (page number)"
+if (breadcrumbList[0] === "posts") {
+ breadcrumbList.splice(
+ 0,
+ 2,
+ `${t.nav.posts} (${t.pagination.page.toLowerCase()} ${breadcrumbList[1] || 1})`
+ );
+}
+
+// if breadcrumb is Home > Tags > [tag] > [page]
+// replace [tag] > [page] with localized [tag] (page number)
+if (breadcrumbList[0] === "tags" && !isNaN(Number(breadcrumbList[2]))) {
+ breadcrumbList.splice(
+ 1,
+ 3,
+ `${decodeSegment(breadcrumbList[1])} ${
+ Number(breadcrumbList[2]) === 1
+ ? ""
+ : `(${t.pagination.page.toLowerCase()} ${breadcrumbList[2]})`
+ }`
+ );
+}
+---
+
+
+
+
+
+ {t.nav.home}
+
+ »
+
+ {
+ breadcrumbList.map((breadcrumb, index) =>
+ index + 1 === breadcrumbList.length ? (
+
+ 0 }]}
+ aria-current="page"
+ >
+ {/* make the last part lowercase in Home > Tags > some-tag */}
+ {navLabels[breadcrumb] ?? decodeSegment(breadcrumb)}
+
+
+ ) : (
+
+
+ {navLabels[breadcrumb] ?? decodeSegment(breadcrumb)}
+
+
+ »
+
+
+ )
+ )
+ }
+
+
diff --git a/src/components/Card.astro b/src/components/Card.astro
new file mode 100644
index 0000000..323757e
--- /dev/null
+++ b/src/components/Card.astro
@@ -0,0 +1,31 @@
+---
+import type { CollectionEntry } from "astro:content";
+import { getPostUrl } from "@/utils/getPostPaths";
+import { toTransitionName } from "@/utils/toTransitionName";
+import Datetime from "./Datetime.astro";
+
+type Props = {
+ variant?: "h2" | "h3";
+} & CollectionEntry<"posts">;
+
+const { variant: Heading = "h2", id, data, filePath } = Astro.props;
+
+const { title, description, ...props } = data;
+---
+
+
+
+
+ {title}
+
+
+
+ {description}
+
diff --git a/src/components/Datetime.astro b/src/components/Datetime.astro
new file mode 100644
index 0000000..adb793d
--- /dev/null
+++ b/src/components/Datetime.astro
@@ -0,0 +1,62 @@
+---
+import dayjs from "dayjs";
+import utc from "dayjs/plugin/utc";
+import timezone from "dayjs/plugin/timezone";
+import IconCalendar from "@/assets/icons/IconCalendar.svg";
+import config from "@/config";
+import { useTranslations } from "@/i18n";
+import { resolveTimezone } from "@/utils/resolveTimezone";
+
+dayjs.extend(utc);
+dayjs.extend(timezone);
+
+type Props = {
+ class?: string;
+ size?: "sm" | "lg";
+ pubDatetime: string | Date;
+ timezone?: string;
+ modDatetime?: string | Date | null;
+};
+
+const {
+ pubDatetime,
+ modDatetime,
+ size = "sm",
+ class: className = "",
+ timezone: postTimezone,
+} = Astro.props;
+
+const t = useTranslations(Astro.currentLocale);
+
+const isModified = modDatetime && modDatetime > pubDatetime;
+
+const datetime = dayjs(isModified ? modDatetime : pubDatetime).tz(
+ resolveTimezone(postTimezone ?? config.site.timezone)
+);
+
+const date = datetime.format("D MMM, YYYY");
+---
+
+
+
+ {
+ isModified && (
+
+ {t.post.updatedAt}:
+
+ )
+ }
+
+ {date}
+
+
diff --git a/src/components/Footer.astro b/src/components/Footer.astro
new file mode 100644
index 0000000..9f4f3d6
--- /dev/null
+++ b/src/components/Footer.astro
@@ -0,0 +1,30 @@
+---
+import type { HTMLAttributes } from "astro/types";
+import Socials from "./Socials.astro";
+import { useTranslations } from "@/i18n";
+
+type Props = {
+ noMarginTop?: boolean;
+} & HTMLAttributes<"footer">;
+
+const { noMarginTop = false, class: className, ...attrs } = Astro.props;
+
+const t = useTranslations(Astro.currentLocale);
+const currentYear = new Date().getFullYear();
+---
+
+
diff --git a/src/components/Header.astro b/src/components/Header.astro
new file mode 100644
index 0000000..9b494d3
--- /dev/null
+++ b/src/components/Header.astro
@@ -0,0 +1,226 @@
+---
+import { getRelativeLocaleUrl } from "astro:i18n";
+import IconUnderline from "@/assets/icons/IconUnderline.svg";
+import IconMenuDeep from "@/assets/icons/IconMenuDeep.svg";
+import IconArchive from "@/assets/icons/IconArchive.svg";
+import IconSunHigh from "@/assets/icons/IconSunHigh.svg";
+import IconSearch from "@/assets/icons/IconSearch.svg";
+import IconMoon from "@/assets/icons/IconMoon.svg";
+import IconX from "@/assets/icons/IconX.svg";
+import LinkButton from "./LinkButton.astro";
+import { stripBase, stripLocale } from "@/utils/withBase";
+import { useTranslations } from "@/i18n";
+import config from "@/config";
+
+const { site, features } = config;
+
+const locale = Astro.currentLocale ?? config.site.lang;
+const t = useTranslations(locale);
+
+// Strip the base prefix so isActive comparisons work regardless of deploy subdirectory.
+// e.g. with base "/blog": "/blog/posts/slug" → "/posts/slug"
+const relativePath = stripBase(Astro.url.pathname);
+const pathWithoutTrailingSlash =
+ relativePath.endsWith("/") && relativePath !== "/"
+ ? relativePath.slice(0, -1)
+ : relativePath;
+
+const currentPath = stripLocale(pathWithoutTrailingSlash, locale);
+
+const isActive = (path: string) => {
+ const currentPathArray = currentPath.split("/").filter(p => p.trim());
+ const pathArray = path.split("/").filter(p => p.trim());
+ return currentPath === path || currentPathArray[0] === pathArray[0];
+};
+---
+
+
+ {t.a11y.skipToContent}
+
+
+
+
+
diff --git a/src/components/LinkButton.astro b/src/components/LinkButton.astro
new file mode 100644
index 0000000..cfcc113
--- /dev/null
+++ b/src/components/LinkButton.astro
@@ -0,0 +1,21 @@
+---
+import type { HTMLAttributes } from "astro/types";
+
+type Props = { disabled?: boolean } & HTMLAttributes<"a">;
+
+const { disabled, class: className, ...attrs } = Astro.props;
+
+const Button = disabled ? "span" : "a";
+---
+
+
+
+
diff --git a/src/components/Main.astro b/src/components/Main.astro
new file mode 100644
index 0000000..af65898
--- /dev/null
+++ b/src/components/Main.astro
@@ -0,0 +1,38 @@
+---
+import { getRelativeLocaleUrl } from "astro:i18n";
+import config from "@/config";
+
+type Props = {
+ pageTitle: string | [string, string];
+ pageDesc?: string;
+ class?: string;
+};
+
+const { pageTitle, pageDesc, class: className } = Astro.props;
+
+const locale = Astro.currentLocale ?? config.site.lang;
+const backUrl = config.features.showBackButton
+ ? Astro.url.pathname
+ : getRelativeLocaleUrl(locale, "");
+---
+
+
+ {pageTitle}
+ {pageDesc}
+
+
+
+
diff --git a/src/components/Pagination.astro b/src/components/Pagination.astro
new file mode 100644
index 0000000..810825d
--- /dev/null
+++ b/src/components/Pagination.astro
@@ -0,0 +1,46 @@
+---
+import type { Page } from "astro";
+import type { CollectionEntry } from "astro:content";
+import IconArrowLeft from "@/assets/icons/IconArrowLeft.svg";
+import IconArrowRight from "@/assets/icons/IconArrowRight.svg";
+import LinkButton from "./LinkButton.astro";
+import { useTranslations } from "@/i18n";
+
+type Props = {
+ page: Page>;
+};
+
+const { page } = Astro.props;
+
+const t = useTranslations(Astro.currentLocale);
+---
+
+{
+ page.lastPage > 1 && (
+
+
+
+ {t.pagination.prev}
+
+ {page.currentPage} / {page.lastPage}
+
+ {t.pagination.next}
+
+
+
+ )
+}
diff --git a/src/components/ResponsiveTable.astro b/src/components/ResponsiveTable.astro
new file mode 100644
index 0000000..8adb2c6
--- /dev/null
+++ b/src/components/ResponsiveTable.astro
@@ -0,0 +1,38 @@
+---
+type Props = {
+ class?: string;
+ variant?:
+ /** No borders */
+ | "minimal"
+ /** 1st, 3rd, 5th … body rows tinted — classic zebra. */
+ | "striped"
+ /** 1st, 3rd, 5th … body rows tinted + no borders. */
+ | "striped-minimal";
+};
+
+const { class: className, variant } = Astro.props;
+
+const variantClasses = {
+ minimal: "[&_td]:border-0 [&_th]:border-0",
+ striped: "[&_tbody_tr]:odd:bg-muted/25",
+};
+---
+
+
diff --git a/src/components/Socials.astro b/src/components/Socials.astro
new file mode 100644
index 0000000..8278330
--- /dev/null
+++ b/src/components/Socials.astro
@@ -0,0 +1,36 @@
+---
+import config from "@/config";
+import LinkButton from "./LinkButton.astro";
+
+const { site, socials } = config;
+
+const icons = import.meta.glob<{
+ default: (_props: Record) => unknown;
+}>("/src/assets/icons/socials/*.svg");
+
+const items = await Promise.all(
+ socials.map(async ({ name, url, linkTitle }) => {
+ const mod = await icons[`/src/assets/icons/socials/${name}.svg`]?.();
+ const Icon = mod?.default;
+ const title =
+ linkTitle ??
+ (url.startsWith("mailto:") || name === "mail"
+ ? `Send an email to ${site.title}`
+ : `${site.title} on ${name.charAt(0).toUpperCase() + name.slice(1)}`);
+ return { url, title, Icon };
+ })
+);
+---
+
+
+ {
+ items.map(({ url, title, Icon }) =>
+ Icon ? (
+
+
+ {title}
+
+ ) : null
+ )
+ }
+
diff --git a/src/components/Tag.astro b/src/components/Tag.astro
new file mode 100644
index 0000000..9d2ad9f
--- /dev/null
+++ b/src/components/Tag.astro
@@ -0,0 +1,39 @@
+---
+import { getRelativeLocaleUrl } from "astro:i18n";
+import IconHash from "@/assets/icons/IconHash.svg";
+import config from "@/config";
+
+type Props = {
+ tag: string;
+ tagName: string;
+ size?: "sm" | "lg";
+};
+
+const { tag, tagName, size = "lg" } = Astro.props;
+
+const locale = Astro.currentLocale ?? config.site.lang;
+---
+
+
+
+
+ {tagName}
+
+
diff --git a/src/config.ts b/src/config.ts
new file mode 100644
index 0000000..10b19fd
--- /dev/null
+++ b/src/config.ts
@@ -0,0 +1,42 @@
+/**
+ * Internal resolved configuration used throughout the codebase.
+ *
+ * Prefer editing `astro-paper.config.ts` instead of this file. This module exists to
+ * apply defaults and expose a fully-resolved config shape (`ResolvedAstroPaperConfig`).
+ */
+import userConfig from "@/astro-paper.config";
+import type { ResolvedAstroPaperConfig } from "./types/config";
+import { PUBLIC_GOOGLE_SITE_VERIFICATION } from "astro:env/client";
+
+const DEFAULT_OG_IMAGE = "default-og.jpg";
+
+const config: ResolvedAstroPaperConfig = {
+ site: {
+ ...userConfig.site,
+ ogImage: userConfig.site.ogImage ?? DEFAULT_OG_IMAGE,
+ lang: userConfig.site.lang ?? "en",
+ timezone: userConfig.site.timezone ?? "UTC",
+ dir: userConfig.site.dir ?? "ltr",
+ googleVerification:
+ userConfig.site.googleVerification || PUBLIC_GOOGLE_SITE_VERIFICATION,
+ },
+ posts: {
+ perPage: userConfig.posts?.perPage ?? 4,
+ perIndex: userConfig.posts?.perIndex ?? 4,
+ // Deprecated and unused. Not read from userConfig, so builds don't warn about it.
+ scheduledPostMargin: 15 * 60 * 1000,
+ },
+ features: {
+ lightAndDarkMode: userConfig.features?.lightAndDarkMode ?? true,
+ dynamicOgImage: userConfig.features?.dynamicOgImage ?? true,
+ showArchives: userConfig.features?.showArchives ?? true,
+ showBackButton: userConfig.features?.showBackButton ?? true,
+ editPost: userConfig.features?.editPost ?? { enabled: false },
+ search: userConfig.features?.search ?? "pagefind",
+ toc: userConfig.features?.toc ?? true,
+ },
+ socials: userConfig.socials ?? [],
+ shareLinks: userConfig.shareLinks ?? [],
+};
+
+export default config;
diff --git a/src/content.config.ts b/src/content.config.ts
new file mode 100644
index 0000000..9ccc058
--- /dev/null
+++ b/src/content.config.ts
@@ -0,0 +1,42 @@
+import { defineCollection } from "astro:content";
+import { z } from "astro/zod";
+import { glob } from "astro/loaders";
+import config from "@/config";
+
+export const BLOG_PATH = "src/content/posts";
+
+const posts = defineCollection({
+ loader: glob({ pattern: "**/[^_]*.{md,mdx}", base: `./${BLOG_PATH}` }),
+ schema: ({ image }) =>
+ z.object({
+ author: z.string().default(config.site.author),
+ pubDatetime: z.date(),
+ modDatetime: z.date().optional().nullable(),
+ title: z.string(),
+ featured: z.boolean().optional(),
+ draft: z.boolean().optional(),
+ tags: z.array(z.string()).default(["others"]),
+ ogImage: image().or(z.string()).optional(),
+ description: z.string(),
+ canonicalURL: z.string().optional(),
+ hideEditPost: z.boolean().optional(),
+ hideToc: z.boolean().optional(),
+ // An empty value means "not set", so the site timezone applies.
+ timezone: z
+ .string()
+ .optional()
+ .transform(tz => tz || undefined),
+ }),
+});
+
+const pages = defineCollection({
+ loader: glob({ pattern: "**/[^_]*.{md,mdx}", base: "./src/content/pages" }),
+ schema: z.object({
+ title: z.string(),
+ description: z.string().optional(),
+ ogImage: z.string().optional(),
+ canonicalURL: z.string().optional(),
+ }),
+});
+
+export const collections = { posts, pages };
diff --git a/about.md b/src/content/pages/about.md
similarity index 84%
rename from about.md
rename to src/content/pages/about.md
index 7fb524c..fec1b60 100644
--- a/about.md
+++ b/src/content/pages/about.md
@@ -1,8 +1,6 @@
---
-layout: page
-title: About
-description: Fractional CTO and engineering lead helping startups and scaleups accelerate and get to revenue.
-permalink: /about/
+title: "About"
+description: "Fractional CTO and engineering lead helping startups and scaleups accelerate and get to revenue."
---
I'm Sean, a partner at [ExaDev](https://exadev.io). I work as a fractional CTO
@@ -34,8 +32,7 @@ process.
## Outside client work
I like to understand how things work at a low level, which usually means
-taking them apart. The [blog]({{ '/blog/' | relative_url }}) is where I write
-that up.
+taking them apart. The [blog](/posts/) is where I write that up.
## Get in touch
diff --git a/_posts/2023-08-15-CAN-Bus-Hacking.markdown b/src/content/posts/can-bus-hacking.md
similarity index 96%
rename from _posts/2023-08-15-CAN-Bus-Hacking.markdown
rename to src/content/posts/can-bus-hacking.md
index 4e6e6a4..cef3734 100644
--- a/_posts/2023-08-15-CAN-Bus-Hacking.markdown
+++ b/src/content/posts/can-bus-hacking.md
@@ -1,10 +1,14 @@
---
-layout: post
-title: "CAN Bus Hacking"
-date: 2023-08-15 22:54:12 +0000
+title: "CAN Bus Hacking"
description: "Using an ESP32 on a BMW F30's CAN bus to make steering wheel controls work with an aftermarket head unit."
-categories: automotive can bmw F30
+pubDatetime: 2023-08-15T22:54:12Z
+tags:
+ - automotive
+ - can
+ - bmw
+ - f30
---
+
Forgive the overzealous use of the word "hacking" - it's hardly that.
I use a drop-in replacement head unit display for our 2013 (F30) 318d Touring.
diff --git a/_posts/2026-11-09-decoding-a-hot-tub.markdown b/src/content/posts/decoding-a-hot-tub.md
similarity index 96%
rename from _posts/2026-11-09-decoding-a-hot-tub.markdown
rename to src/content/posts/decoding-a-hot-tub.md
index 6a346dc..5464717 100644
--- a/_posts/2026-11-09-decoding-a-hot-tub.markdown
+++ b/src/content/posts/decoding-a-hot-tub.md
@@ -1,12 +1,14 @@
---
-layout: post
title: "Decoding a hot tub: the Jacuzzi \"encrypted\" RS485 protocol"
description: "The encryption on a Jacuzzi J235's control bus is one random byte and a fixed key stream. How I worked that out, with a decoder that runs in your browser."
-date: 2026-11-09 12:00:00 +0000
-categories: home-automation rs485 reverse-engineering
-scripts:
- - /assets/js/spa-decoder.js
+pubDatetime: 2026-11-09T12:00:00Z
+featured: true
+tags:
+ - home-automation
+ - rs485
+ - reverse-engineering
---
+
I have a Jacuzzi J235 in the garden and a project called
[spa2mqtt](https://github.com/srleach/spa2mqtt) that connects its control bus to Home
Assistant. Last week I rebuilt it from the ground up and released it as v2.0.0. Along the
@@ -136,9 +138,11 @@ Paste hex from the bus, or pick a sample. Everything runs in your browser. The f
real: the J235 ones are from my tub, and the other two were published by the Sundance and
pyjacuzzi projects.
-
+
+
+
The decoding logic is about 150 lines of JavaScript with no dependencies, ported from the
Python in spa2mqtt. You can read it at [/assets/js/spa-decoder.js](/assets/js/spa-decoder.js).
@@ -206,7 +210,7 @@ and no container in between.
A few fields remain unexplained: two maintenance counters, a manual circulation bit I have
never seen set, and a display code whose values I can't yet map. The
-[protocol reference](https://srleach.github.io/spa2mqtt/protocol/) lists every claim with
+[protocol reference](https://seanrleach.co.uk/spa2mqtt/protocol/) lists every claim with
its evidence and status, including the ones I'm unsure of.
If you have a Balboa-family tub, encrypted or not, the capture tool and a labelled session
@@ -225,4 +229,4 @@ for the fold, the button codes and the lights map,
for showing it could be done on an ESP32.
The code is at [github.com/srleach/spa2mqtt](https://github.com/srleach/spa2mqtt), and the
-documentation is at [srleach.github.io/spa2mqtt](https://srleach.github.io/spa2mqtt/).
+documentation is at [seanrleach.co.uk/spa2mqtt](https://seanrleach.co.uk/spa2mqtt/).
diff --git a/src/i18n/format.ts b/src/i18n/format.ts
new file mode 100644
index 0000000..8b0fa56
--- /dev/null
+++ b/src/i18n/format.ts
@@ -0,0 +1,13 @@
+/**
+ * Replace `{{key}}` placeholders in UI strings.
+ * Translators can reorder placeholders freely within the sentence.
+ */
+export function tplStr(
+ template: string,
+ vars: Record
+): string {
+ return template.replace(/\{\{(\w+)\}\}/g, (_, key: string) => {
+ const value = vars[key];
+ return value !== undefined && value !== null ? String(value) : "";
+ });
+}
diff --git a/src/i18n/index.ts b/src/i18n/index.ts
new file mode 100644
index 0000000..8274db8
--- /dev/null
+++ b/src/i18n/index.ts
@@ -0,0 +1,28 @@
+import type { UIStrings } from "./types";
+
+export { tplStr } from "./format";
+
+const modules = import.meta.glob<{ default: UIStrings }>("./lang/*.ts", {
+ eager: true,
+});
+
+const translations: Record = {};
+for (const [path, mod] of Object.entries(modules)) {
+ const locale = path.slice("./lang/".length, -".ts".length);
+ translations[locale] = mod.default;
+}
+
+type ResolvedStrings = { [K in keyof UIStrings]: Required };
+
+/**
+ * Returns UI strings for the given locale, falling back to English. Labels that
+ * are optional until v8 fall back one by one.
+ */
+export function useTranslations(locale: string = "en"): ResolvedStrings {
+ const t = translations[locale] ?? translations["en"];
+ return {
+ ...t,
+ post: { tableOfContents: "Table of contents", ...t.post },
+ a11y: { skipTableOfContents: "Skip table of contents", ...t.a11y },
+ };
+}
diff --git a/src/i18n/lang/en.ts b/src/i18n/lang/en.ts
new file mode 100644
index 0000000..0a6589f
--- /dev/null
+++ b/src/i18n/lang/en.ts
@@ -0,0 +1,73 @@
+import type { UIStrings } from "../types";
+
+export default {
+ nav: {
+ home: "Home",
+ posts: "Posts",
+ tags: "Tags",
+ about: "About",
+ archives: "Archives",
+ search: "Search",
+ },
+ post: {
+ publishedAt: "Published at",
+ updatedAt: "Updated",
+ sharePostIntro: "Share this post:",
+ sharePostOn: "Share this post on {{platform}}",
+ sharePostViaEmail: "Share this post via email",
+ tagLabel: "Tags",
+ backToTop: "Back to top",
+ tableOfContents: "Table of contents",
+ goBack: "Go back",
+ editPage: "Edit page",
+ previousPost: "Previous Post",
+ nextPost: "Next Post",
+ },
+ pagination: {
+ prev: "Prev",
+ next: "Next",
+ page: "Page",
+ },
+ home: {
+ socialLinks: "Social Links",
+ featured: "Featured",
+ recentPosts: "Recent Posts",
+ allPosts: "All Posts",
+ },
+ footer: {
+ copyright: "Copyright",
+ allRightsReserved: "All rights reserved.",
+ },
+ pages: {
+ tagTitle: "Tag",
+ tagDesc: "All the articles with the tag",
+
+ tagsTitle: "Tags",
+ tagsDesc: "All the tags used in posts.",
+
+ postsTitle: "Posts",
+ postsDesc: "All the articles I've posted.",
+
+ archivesTitle: "Archives",
+ archivesDesc: "All the articles I've archived.",
+
+ searchTitle: "Search",
+ searchDesc: "Search any article ...",
+ },
+ a11y: {
+ skipToContent: "Skip to content",
+ skipTableOfContents: "Skip table of contents",
+ openMenu: "Open menu",
+ closeMenu: "Close menu",
+ toggleTheme: "Toggle theme",
+ searchPlaceholder: "Search posts...",
+ noResults: "No results found",
+ goToPreviousPage: "Go to previous page",
+ goToNextPage: "Go to next page",
+ },
+ notFound: {
+ title: "404 Not Found",
+ message: "Page Not Found",
+ goHome: "Go back home",
+ },
+} satisfies UIStrings;
diff --git a/src/i18n/types.ts b/src/i18n/types.ts
new file mode 100644
index 0000000..7f0a2ff
--- /dev/null
+++ b/src/i18n/types.ts
@@ -0,0 +1,73 @@
+export interface UIStrings {
+ nav: {
+ home: string;
+ posts: string;
+ tags: string;
+ about: string;
+ archives: string;
+ search: string;
+ };
+ post: {
+ publishedAt: string;
+ updatedAt: string;
+ sharePostIntro: string;
+ sharePostOn: string;
+ sharePostViaEmail: string;
+ tagLabel: string;
+ backToTop: string;
+ /** Optional until v8, so older locale files still type-check. */
+ tableOfContents?: string;
+ goBack: string;
+ editPage: string;
+ previousPost: string;
+ nextPost: string;
+ };
+ pagination: {
+ prev: string;
+ next: string;
+ page: string;
+ };
+ home: {
+ socialLinks: string;
+ featured: string;
+ recentPosts: string;
+ allPosts: string;
+ };
+ footer: {
+ copyright: string;
+ allRightsReserved: string;
+ };
+ pages: {
+ tagTitle: string;
+ tagDesc: string;
+
+ tagsTitle: string;
+ tagsDesc: string;
+
+ postsTitle: string;
+ postsDesc: string;
+
+ archivesTitle: string;
+ archivesDesc: string;
+
+ searchTitle: string;
+ searchDesc: string;
+ };
+ a11y: {
+ skipToContent: string;
+ /** Optional until v8, so older locale files still type-check. */
+ skipTableOfContents?: string;
+ openMenu: string;
+ closeMenu: string;
+ toggleTheme: string;
+ searchPlaceholder: string;
+ noResults: string;
+ goToPreviousPage: string;
+ goToNextPage: string;
+ };
+ notFound: {
+ title: string;
+ message: string;
+ goHome: string;
+ };
+}
diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro
new file mode 100644
index 0000000..5ddd361
--- /dev/null
+++ b/src/layouts/Layout.astro
@@ -0,0 +1,123 @@
+---
+import { Font } from "astro:assets";
+import { ClientRouter } from "astro:transitions";
+import { resolveDefaultOgImagePath } from "@/utils/resolveDefaultOgImagePath";
+import { getAssetPath } from "@/utils/withBase";
+import config from "@/config";
+import "@/styles/global.css";
+
+type Props = {
+ title?: string;
+ description?: string;
+ ogImage?: string;
+ canonicalURL?: string;
+};
+
+const { site } = config;
+
+const {
+ title = site.title,
+ description = site.description,
+ ogImage = resolveDefaultOgImagePath(config),
+ canonicalURL = new URL(Astro.url.pathname, Astro.site).href,
+} = Astro.props;
+
+const socialImageURL = new URL(ogImage, Astro.site ?? Astro.url);
+const rssHref = getAssetPath("rss.xml");
+---
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ site.googleVerification && (
+
+ )
+ }
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/layouts/PostLayout.astro b/src/layouts/PostLayout.astro
new file mode 100644
index 0000000..f2b1a61
--- /dev/null
+++ b/src/layouts/PostLayout.astro
@@ -0,0 +1,73 @@
+---
+import Layout from "./Layout.astro";
+import config from "@/config";
+
+type Props = {
+ title?: string;
+ description?: string;
+ ogImage?: string;
+ canonicalURL?: string;
+ pubDatetime?: Date;
+ modDatetime?: Date | null;
+};
+
+const { site } = config;
+
+const { title, description, ogImage, canonicalURL, pubDatetime, modDatetime } =
+ Astro.props;
+
+const structuredData = {
+ "@context": "https://schema.org",
+ "@type": "BlogPosting",
+ headline: title ?? site.title,
+ image: ogImage,
+ ...(pubDatetime && { datePublished: pubDatetime.toISOString() }),
+ ...(modDatetime && { dateModified: modDatetime.toISOString() }),
+ author: [
+ {
+ "@type": "Person",
+ name: site.author,
+ ...(site.profile && { url: site.profile }),
+ },
+ ],
+};
+
+// JSON.stringify leaves `<` alone, so a title containing `` would
+// end the ld+json block early. `\u003c` reads back as `<` for JSON parsers.
+const structuredDataJson = JSON.stringify(structuredData).replace(
+ /
+
+
+
+
+ {
+ pubDatetime && (
+
+ )
+ }
+ {
+ modDatetime && (
+
+ )
+ }
+
+
+
+
+
+
diff --git a/src/pages/404.astro b/src/pages/404.astro
new file mode 100644
index 0000000..9cd0c29
--- /dev/null
+++ b/src/pages/404.astro
@@ -0,0 +1,35 @@
+---
+import Layout from "@/layouts/Layout.astro";
+import Header from "@/components/Header.astro";
+import Footer from "@/components/Footer.astro";
+import LinkButton from "@/components/LinkButton.astro";
+import { getRelativeLocaleUrl } from "astro:i18n";
+import { useTranslations } from "@/i18n";
+import config from "@/config";
+
+const locale = Astro.currentLocale ?? config.site.lang;
+const t = useTranslations(locale);
+---
+
+
+
+
+
+
+
404
+
¯\_(ツ)_/¯
+
{t.notFound.message}
+
+ {t.notFound.goHome}
+
+
+
+
+
+
diff --git a/src/pages/about.astro b/src/pages/about.astro
new file mode 100644
index 0000000..ec85ba2
--- /dev/null
+++ b/src/pages/about.astro
@@ -0,0 +1,36 @@
+---
+import { getEntry, render } from "astro:content";
+import Layout from "@/layouts/Layout.astro";
+import Header from "@/components/Header.astro";
+import Breadcrumb from "@/components/Breadcrumb.astro";
+import Main from "@/components/Main.astro";
+import Footer from "@/components/Footer.astro";
+import config from "@/config";
+
+const about = await getEntry("pages", "about");
+
+if (!about) {
+ throw new Error(
+ "Missing content entry: `about.md` or `about.mdx` in `src/content/pages/`"
+ );
+}
+
+const { Content } = await render(about);
+---
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/archives/_utils/getPostsByGroupCondition.ts b/src/pages/archives/_utils/getPostsByGroupCondition.ts
new file mode 100644
index 0000000..3152c13
--- /dev/null
+++ b/src/pages/archives/_utils/getPostsByGroupCondition.ts
@@ -0,0 +1,24 @@
+import type { CollectionEntry } from "astro:content";
+
+type GroupKey = string | number | symbol;
+type GroupFunction = (item: T, index?: number) => GroupKey;
+
+export function getPostsByGroupCondition(
+ posts: CollectionEntry<"posts">[],
+ groupFunction: GroupFunction>
+) {
+ const result: Record[]> = {};
+
+ for (let i = 0; i < posts.length; i++) {
+ const item = posts[i];
+ const groupKey = groupFunction(item, i);
+
+ if (!result[groupKey]) {
+ result[groupKey] = [];
+ }
+
+ result[groupKey].push(item);
+ }
+
+ return result;
+}
diff --git a/src/pages/archives/index.astro b/src/pages/archives/index.astro
new file mode 100644
index 0000000..0aa0f83
--- /dev/null
+++ b/src/pages/archives/index.astro
@@ -0,0 +1,103 @@
+---
+import { getRelativeLocaleUrl } from "astro:i18n";
+import { getCollection, type CollectionEntry } from "astro:content";
+import dayjs from "dayjs";
+import utc from "dayjs/plugin/utc";
+import timezone from "dayjs/plugin/timezone";
+import Layout from "@/layouts/Layout.astro";
+import Header from "@/components/Header.astro";
+import Breadcrumb from "@/components/Breadcrumb.astro";
+import Main from "@/components/Main.astro";
+import Footer from "@/components/Footer.astro";
+import Card from "@/components/Card.astro";
+import { postFilter } from "@/utils/postFilter";
+import { resolveTimezone } from "@/utils/resolveTimezone";
+import { getPostsByGroupCondition } from "./_utils/getPostsByGroupCondition";
+import { useTranslations } from "@/i18n";
+import config from "@/config";
+
+dayjs.extend(utc);
+dayjs.extend(timezone);
+
+const locale = Astro.currentLocale ?? config.site.lang;
+
+// Redirect to 404 page if `showArchives` config is false
+const notFoundUrl = getRelativeLocaleUrl(locale, "404");
+if (!config.features.showArchives && notFoundUrl) {
+ return Astro.rewrite(notFoundUrl);
+}
+
+const t = useTranslations(locale);
+
+const posts = await getCollection("posts");
+const filteredPosts = posts.filter(postFilter);
+const monthFormatter = new Intl.DateTimeFormat(locale, { month: "long" });
+
+// Group by the calendar day the post's byline shows (see Datetime.astro),
+// not by the build machine's local zone.
+const publishDate = (post: CollectionEntry<"posts">) =>
+ dayjs(post.data.pubDatetime).tz(
+ resolveTimezone(post.data.timezone ?? config.site.timezone)
+ );
+---
+
+
+
+
+
+
+
+ {
+ Object.entries(
+ getPostsByGroupCondition(filteredPosts, post =>
+ publishDate(post).year()
+ )
+ )
+ .sort(([yearA], [yearB]) => Number(yearB) - Number(yearA))
+ .map(([year, yearGroup]) => (
+
+
{year} {" "}
+
{yearGroup.length}
+ {Object.entries(
+ getPostsByGroupCondition(
+ yearGroup,
+ post => publishDate(post).month() + 1
+ )
+ )
+ .sort(([monthA], [monthB]) => Number(monthB) - Number(monthA))
+ .map(([month, monthGroup]) => (
+
+
+
+ {monthFormatter.format(
+ new Date(2000, Number(month) - 1, 1)
+ )}
+ {" "}
+
+ {monthGroup.length}
+
+
+
+ {monthGroup
+ .sort(
+ (a, b) =>
+ Math.floor(
+ new Date(b.data.pubDatetime).getTime() / 1000
+ ) -
+ Math.floor(
+ new Date(a.data.pubDatetime).getTime() / 1000
+ )
+ )
+ .map(data => (
+
+ ))}
+
+
+ ))}
+
+ ))
+ }
+
+
+
+
diff --git a/src/pages/feed.xml.ts b/src/pages/feed.xml.ts
new file mode 100644
index 0000000..c9405c5
--- /dev/null
+++ b/src/pages/feed.xml.ts
@@ -0,0 +1,2 @@
+// The Jekyll site published its feed at /feed.xml. Keep that URL working.
+export { GET } from "./rss.xml";
diff --git a/src/pages/index.astro b/src/pages/index.astro
new file mode 100644
index 0000000..b5ac1d3
--- /dev/null
+++ b/src/pages/index.astro
@@ -0,0 +1,139 @@
+---
+import { getCollection } from "astro:content";
+import { getRelativeLocaleUrl } from "astro:i18n";
+import IconRss from "@/assets/icons/IconRss.svg";
+import IconArrowRight from "@/assets/icons/IconArrowRight.svg";
+import Layout from "@/layouts/Layout.astro";
+import Header from "@/components/Header.astro";
+import Footer from "@/components/Footer.astro";
+import Socials from "@/components/Socials.astro";
+import Card from "@/components/Card.astro";
+import LinkButton from "@/components/LinkButton.astro";
+import { getSortedPosts } from "@/utils/getSortedPosts";
+import { useTranslations } from "@/i18n";
+import config from "@/config";
+
+const { socials, posts: postsConfig } = config;
+
+const locale = Astro.currentLocale ?? config.site.lang;
+const t = useTranslations(locale);
+
+const posts = await getCollection("posts");
+const sortedPosts = getSortedPosts(posts);
+const featuredPosts = sortedPosts.filter(({ data }) => data.featured);
+const recentPosts = sortedPosts.filter(({ data }) => !data.featured);
+const homePath = getRelativeLocaleUrl(locale, "");
+---
+
+
+
+
+
+
+ Sean Leach
+ {" "}
+
+
+ RSS Feed
+
+
+
+ I'm a fractional CTO and engineering lead, and a partner at ExaDev. I
+ help startups and scaleups build faster and get to revenue: setting
+ technical direction, building and leading the engineering team, and
+ shipping alongside them.
+
+
+ This is also where I write up the technical projects I take apart.
+ Read the posts, or see{" "}
+
+ what I do
+
+ .
+
+ {
+ socials.length > 0 && (
+
+
+ {t.home.socialLinks}:
+
+
+
+ )
+ }
+
+
+ {
+ featuredPosts.length > 0 && (
+ 0 },
+ ]}
+ >
+
+ {t.home.featured}
+
+
+ {featuredPosts.map(data => (
+
+ ))}
+
+
+ )
+ }
+
+ {
+ recentPosts.length > 0 && (
+
+
+ {t.home.recentPosts}
+
+
+ {recentPosts.slice(0, postsConfig.perIndex).map(data => (
+
+ ))}
+
+
+ )
+ }
+
+
+
+ {t.home.allPosts}
+
+
+
+
+
+
+
+
diff --git a/src/pages/og.png.ts b/src/pages/og.png.ts
new file mode 100644
index 0000000..f7cd5ce
--- /dev/null
+++ b/src/pages/og.png.ts
@@ -0,0 +1,177 @@
+import type { APIRoute } from "astro";
+import satori from "satori";
+import sharp from "sharp";
+import { fontData, experimental_getFontFileURL } from "astro:assets";
+import { getFontPathByWeight } from "@/utils/getFontPathByWeight";
+import config from "@/config";
+
+export const GET: APIRoute = async context => {
+ const fonts = fontData["--font-google-sans-code"];
+ const regularFontPath = getFontPathByWeight(fonts, 400);
+ const boldFontPath = getFontPathByWeight(fonts, 700);
+
+ if (regularFontPath === undefined || boldFontPath === undefined) {
+ throw new Error("Cannot find the font path.");
+ }
+
+ const [regularData, boldData] = await Promise.all([
+ fetch(experimental_getFontFileURL(regularFontPath, context.url)).then(res =>
+ res.arrayBuffer()
+ ),
+ fetch(experimental_getFontFileURL(boldFontPath, context.url)).then(res =>
+ res.arrayBuffer()
+ ),
+ ]);
+
+ const svg = await satori(
+ {
+ key: null,
+ type: "div",
+ props: {
+ style: {
+ background: "#fefbfb",
+ width: "100%",
+ height: "100%",
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "center",
+ fontFamily: "Google Sans Code",
+ },
+ children: [
+ {
+ type: "div",
+ props: {
+ style: {
+ position: "absolute",
+ top: "-1px",
+ right: "-1px",
+ boxSizing: "border-box",
+ border: "4px solid #000",
+ background: "#ecebeb",
+ opacity: "0.9",
+ borderRadius: "4px",
+ display: "flex",
+ justifyContent: "center",
+ margin: "2.5rem",
+ width: "88%",
+ height: "80%",
+ },
+ },
+ },
+ {
+ type: "div",
+ props: {
+ style: {
+ position: "relative",
+ boxSizing: "border-box",
+ border: "4px solid #000",
+ background: "#fefbfb",
+ borderRadius: "4px",
+ display: "flex",
+ justifyContent: "center",
+ margin: "2rem",
+ width: "88%",
+ height: "80%",
+ },
+ children: {
+ type: "div",
+ props: {
+ style: {
+ display: "flex",
+ flexDirection: "column",
+ justifyContent: "space-between",
+ margin: "20px",
+ width: "90%",
+ height: "90%",
+ },
+ children: [
+ {
+ type: "div",
+ props: {
+ style: {
+ display: "flex",
+ flexDirection: "column",
+ justifyContent: "center",
+ alignItems: "center",
+ height: "90%",
+ maxHeight: "90%",
+ overflow: "hidden",
+ textAlign: "center",
+ },
+ children: [
+ {
+ type: "p",
+ props: {
+ style: {
+ fontSize: 72,
+ fontWeight: "bold",
+ margin: "0 0 31px",
+ width: "100%",
+ },
+ children: config.site.title,
+ },
+ },
+ {
+ type: "p",
+ props: {
+ style: { fontSize: 28, margin: 0, width: "100%" },
+ children: config.site.description,
+ },
+ },
+ ],
+ },
+ },
+ {
+ type: "div",
+ props: {
+ style: {
+ display: "flex",
+ justifyContent: "flex-end",
+ width: "100%",
+ marginBottom: "8px",
+ fontSize: 28,
+ },
+ children: {
+ type: "span",
+ props: {
+ style: { overflow: "hidden", fontWeight: "bold" },
+ children: new URL(config.site.url).hostname,
+ },
+ },
+ },
+ },
+ ],
+ },
+ },
+ },
+ },
+ ],
+ },
+ },
+ {
+ width: 1200,
+ height: 630,
+ embedFont: true,
+ fonts: [
+ {
+ name: "Google Sans Code",
+ data: regularData,
+ weight: 400,
+ style: "normal",
+ },
+ {
+ name: "Google Sans Code",
+ data: boldData,
+ weight: 700,
+ style: "normal",
+ },
+ ],
+ }
+ );
+
+ const pngBuffer = await sharp(Buffer.from(svg)).png().toBuffer();
+
+ return new Response(new Uint8Array(pngBuffer), {
+ headers: { "Content-Type": "image/png" },
+ });
+};
diff --git a/src/pages/posts/[...page].astro b/src/pages/posts/[...page].astro
new file mode 100644
index 0000000..7b896c3
--- /dev/null
+++ b/src/pages/posts/[...page].astro
@@ -0,0 +1,40 @@
+---
+import type { GetStaticPaths } from "astro";
+import { getCollection } from "astro:content";
+import Layout from "@/layouts/Layout.astro";
+import Header from "@/components/Header.astro";
+import Breadcrumb from "@/components/Breadcrumb.astro";
+import Main from "@/components/Main.astro";
+import Card from "@/components/Card.astro";
+import Footer from "@/components/Footer.astro";
+import Pagination from "@/components/Pagination.astro";
+import { getSortedPosts } from "@/utils/getSortedPosts";
+import { useTranslations } from "@/i18n";
+import config from "@/config";
+
+export const getStaticPaths = (async ({ paginate }) => {
+ const posts = await getCollection("posts", ({ data }) => !data.draft);
+ return paginate(getSortedPosts(posts), { pageSize: config.posts.perPage });
+}) satisfies GetStaticPaths;
+
+const { page } = Astro.props;
+
+const locale = Astro.currentLocale ?? config.site.lang;
+const t = useTranslations(locale);
+---
+
+
+
+
+
+
+
+
+ {page.data.map(data => )}
+
+
+
+
+
+
diff --git a/src/pages/posts/[...slug]/_components/AdjacentPostNav.astro b/src/pages/posts/[...slug]/_components/AdjacentPostNav.astro
new file mode 100644
index 0000000..64b9700
--- /dev/null
+++ b/src/pages/posts/[...slug]/_components/AdjacentPostNav.astro
@@ -0,0 +1,53 @@
+---
+import IconArrowLeft from "@/assets/icons/IconArrowLeft.svg";
+import IconArrowRight from "@/assets/icons/IconArrowRight.svg";
+import { getPostUrl } from "@/utils/getPostPaths";
+import { useTranslations } from "@/i18n";
+import config from "@/config";
+
+type AdjacentPost = {
+ id: string;
+ title: string;
+ filePath: string | undefined;
+} | null;
+
+type Props = {
+ prevPost: AdjacentPost;
+ nextPost: AdjacentPost;
+};
+
+const { prevPost, nextPost } = Astro.props;
+const locale = Astro.currentLocale ?? config.site.lang;
+const t = useTranslations(locale);
+---
+
+
diff --git a/src/pages/posts/[...slug]/_components/BackButton.astro b/src/pages/posts/[...slug]/_components/BackButton.astro
new file mode 100644
index 0000000..3c35cdd
--- /dev/null
+++ b/src/pages/posts/[...slug]/_components/BackButton.astro
@@ -0,0 +1,41 @@
+---
+import IconChevronLeft from "@/assets/icons/IconChevronLeft.svg";
+import LinkButton from "@/components/LinkButton.astro";
+import { getRelativeLocaleUrl } from "astro:i18n";
+import { useTranslations } from "@/i18n";
+import config from "@/config";
+
+const locale = Astro.currentLocale ?? config.site.lang;
+const t = useTranslations(locale);
+---
+
+{
+ config.features.showBackButton && (
+
+
+
+ {t.post.goBack}
+
+
+ )
+}
+
+
diff --git a/src/pages/posts/[...slug]/_components/BackToTopButton.astro b/src/pages/posts/[...slug]/_components/BackToTopButton.astro
new file mode 100644
index 0000000..2cd6842
--- /dev/null
+++ b/src/pages/posts/[...slug]/_components/BackToTopButton.astro
@@ -0,0 +1,275 @@
+---
+import type { MarkdownHeading } from "astro";
+import IconArrowNarrowUp from "@/assets/icons/IconArrowNarrowUp.svg";
+import IconListTree from "@/assets/icons/IconListTree.svg";
+import IconX from "@/assets/icons/IconX.svg";
+import { useTranslations } from "@/i18n";
+import { buildTocTree, hasToc } from "@/utils/buildTocTree";
+import TocList from "./TocList.astro";
+
+type Props = { headings?: MarkdownHeading[] };
+
+const { headings = [] } = Astro.props;
+const t = useTranslations(Astro.currentLocale);
+const withToc = hasToc(headings);
+const items = withToc ? buildTocTree(headings) : [];
+---
+
+
+
+
+ {
+ withToc && (
+ <>
+
+
+ >
+ )
+ }
+
+
+
+
+{
+ withToc && (
+
+
+
+ {t.post.tableOfContents}
+
+
+
+
+ {t.post.backToTop}
+
+
+
+ )
+}
+
+
+
+
diff --git a/src/pages/posts/[...slug]/_components/EditPost.astro b/src/pages/posts/[...slug]/_components/EditPost.astro
new file mode 100644
index 0000000..7f5645d
--- /dev/null
+++ b/src/pages/posts/[...slug]/_components/EditPost.astro
@@ -0,0 +1,37 @@
+---
+import type { CollectionEntry } from "astro:content";
+import IconEdit from "@/assets/icons/IconEdit.svg";
+import { useTranslations } from "@/i18n";
+import config from "@/config";
+
+type Props = {
+ hideEditPost?: CollectionEntry<"posts">["data"]["hideEditPost"];
+ class?: string;
+ post: CollectionEntry<"posts">;
+};
+
+const { hideEditPost, post, class: className = "" } = Astro.props;
+
+const editPost = config.features.editPost;
+const href = editPost.enabled ? `${editPost.url}${post.filePath}` : "";
+const showEditPost = editPost.enabled && !hideEditPost && href.trim() !== "";
+
+const t = useTranslations(Astro.currentLocale);
+---
+
+{
+ showEditPost && (
+
+
+ {t.post.editPage}
+
+ )
+}
diff --git a/src/pages/posts/[...slug]/_components/PostToc.astro b/src/pages/posts/[...slug]/_components/PostToc.astro
new file mode 100644
index 0000000..c4f026b
--- /dev/null
+++ b/src/pages/posts/[...slug]/_components/PostToc.astro
@@ -0,0 +1,264 @@
+---
+import type { MarkdownHeading } from "astro";
+import IconArrowNarrowUp from "@/assets/icons/IconArrowNarrowUp.svg";
+import { useTranslations } from "@/i18n";
+import { buildTocTree, hasToc } from "@/utils/buildTocTree";
+import TocList from "./TocList.astro";
+
+type Props = { headings: MarkdownHeading[] };
+
+const { headings } = Astro.props;
+const t = useTranslations(Astro.currentLocale);
+const label = t.post.tableOfContents;
+const items = buildTocTree(headings);
+---
+
+{
+ hasToc(headings) && (
+
+ )
+}
+
+
+
+
diff --git a/src/pages/posts/[...slug]/_components/ShareLinks.astro b/src/pages/posts/[...slug]/_components/ShareLinks.astro
new file mode 100644
index 0000000..a508485
--- /dev/null
+++ b/src/pages/posts/[...slug]/_components/ShareLinks.astro
@@ -0,0 +1,54 @@
+---
+import LinkButton from "@/components/LinkButton.astro";
+import { tplStr, useTranslations } from "@/i18n";
+import config from "@/config";
+
+const { shareLinks } = config;
+const t = useTranslations(Astro.currentLocale);
+
+const icons = import.meta.glob<{
+ default: (_props: Record) => unknown;
+}>("/src/assets/icons/socials/*.svg");
+
+const pageUrl = Astro.url;
+
+const platformLabel = (name: string) =>
+ name.charAt(0).toUpperCase() + name.slice(1);
+
+const items = await Promise.all(
+ shareLinks.map(async ({ name, url, linkTitle }) => {
+ const mod = await icons[`/src/assets/icons/socials/${name}.svg`]?.();
+ const Icon = mod?.default;
+ const title =
+ linkTitle ??
+ (name === "mail"
+ ? t.post.sharePostViaEmail
+ : tplStr(t.post.sharePostOn, { platform: platformLabel(name) }));
+ return { url, title, Icon };
+ })
+);
+---
+
+{
+ items.some(({ Icon }) => Icon) && (
+
+
{t.post.sharePostIntro}
+
+ {items.map(({ url, title, Icon }) =>
+ Icon ? (
+
+
+ {title}
+
+ ) : null
+ )}
+
+
+ )
+}
diff --git a/src/pages/posts/[...slug]/_components/TocList.astro b/src/pages/posts/[...slug]/_components/TocList.astro
new file mode 100644
index 0000000..ea6deb4
--- /dev/null
+++ b/src/pages/posts/[...slug]/_components/TocList.astro
@@ -0,0 +1,18 @@
+---
+import type { TocItem } from "@/utils/buildTocTree";
+
+type Props = { items: TocItem[] };
+
+const { items } = Astro.props;
+---
+
+
+ {
+ items.map(item => (
+
+ {item.text}
+ {item.children.length > 0 && }
+
+ ))
+ }
+
diff --git a/src/pages/posts/[...slug]/index.astro b/src/pages/posts/[...slug]/index.astro
new file mode 100644
index 0000000..c285030
--- /dev/null
+++ b/src/pages/posts/[...slug]/index.astro
@@ -0,0 +1,656 @@
+---
+import { type CollectionEntry, getCollection, render } from "astro:content";
+import PostLayout from "@/layouts/PostLayout.astro";
+import Header from "@/components/Header.astro";
+import Footer from "@/components/Footer.astro";
+import Datetime from "@/components/Datetime.astro";
+import Tag from "@/components/Tag.astro";
+import { getPostSlug, getPostUrl } from "@/utils/getPostPaths";
+import { getSortedPosts } from "@/utils/getSortedPosts";
+import { slugifyStr } from "@/utils/slugify";
+import { toTransitionName } from "@/utils/toTransitionName";
+import EditPost from "./_components/EditPost.astro";
+import ShareLinks from "./_components/ShareLinks.astro";
+import BackButton from "./_components/BackButton.astro";
+import BackToTopButton from "./_components/BackToTopButton.astro";
+import AdjacentPostNav from "./_components/AdjacentPostNav.astro";
+import PostToc from "./_components/PostToc.astro";
+import config from "@/config";
+
+export async function getStaticPaths() {
+ const posts = await getCollection("posts");
+ const sortedPosts = getSortedPosts(posts);
+
+ return sortedPosts.map((post, index) => ({
+ params: { slug: getPostSlug(post.id, post.filePath) },
+ props: {
+ post,
+ // sortedPosts is newest-first, so "older" (prev) is a higher index
+ // and "newer" (next) is a lower index.
+ prevPost:
+ index < sortedPosts.length - 1
+ ? {
+ id: sortedPosts[index + 1].id,
+ title: sortedPosts[index + 1].data.title,
+ filePath: sortedPosts[index + 1].filePath,
+ }
+ : null,
+ nextPost:
+ index > 0
+ ? {
+ id: sortedPosts[index - 1].id,
+ title: sortedPosts[index - 1].data.title,
+ filePath: sortedPosts[index - 1].filePath,
+ }
+ : null,
+ },
+ }));
+}
+
+type AdjacentPost = {
+ id: string;
+ title: string;
+ filePath: string | undefined;
+} | null;
+
+type Props = {
+ post: CollectionEntry<"posts">;
+ prevPost: AdjacentPost;
+ nextPost: AdjacentPost;
+};
+
+const { post, prevPost, nextPost } = Astro.props;
+const locale = Astro.currentLocale ?? config.site.lang;
+
+const {
+ title,
+ description,
+ ogImage: initOgImage,
+ canonicalURL,
+ pubDatetime,
+ modDatetime,
+ timezone,
+ tags,
+ hideEditPost,
+ hideToc,
+} = post.data;
+
+const { Content, headings } = await render(post);
+const tocHeadings = config.features.toc && !hideToc ? headings : [];
+
+let ogImageUrl: string | undefined;
+
+if (typeof initOgImage === "string") {
+ ogImageUrl = initOgImage;
+} else if (initOgImage?.src) {
+ ogImageUrl = initOgImage.src;
+}
+
+if (!ogImageUrl && config.features.dynamicOgImage) {
+ const postUrl = getPostUrl(post.id, post.filePath, locale).replace(
+ /\/+$/,
+ ""
+ );
+ ogImageUrl = `${postUrl}/index.png`;
+}
+
+const ogImage = ogImageUrl
+ ? new URL(ogImageUrl, Astro.url.origin).href
+ : undefined;
+---
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ tags.map(tag => (
+
+ ))
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/posts/[...slug]/index.png.ts b/src/pages/posts/[...slug]/index.png.ts
new file mode 100644
index 0000000..a17608c
--- /dev/null
+++ b/src/pages/posts/[...slug]/index.png.ts
@@ -0,0 +1,195 @@
+import type { APIRoute } from "astro";
+import { getCollection } from "astro:content";
+import { fontData, experimental_getFontFileURL } from "astro:assets";
+import satori from "satori";
+import sharp from "sharp";
+import { getFontPathByWeight } from "@/utils/getFontPathByWeight";
+import { getPostSlug } from "@/utils/getPostPaths";
+import { postFilter } from "@/utils/postFilter";
+import config from "@/config";
+
+export async function getStaticPaths() {
+ if (!config.features.dynamicOgImage) {
+ return [];
+ }
+
+ const posts = await getCollection("posts").then(p =>
+ p.filter(post => postFilter(post) && !post.data.ogImage)
+ );
+
+ return posts.map(post => ({
+ params: { slug: getPostSlug(post.id, post.filePath) },
+ props: post,
+ }));
+}
+
+export const GET: APIRoute = async ({ props, url }) => {
+ if (!config.features.dynamicOgImage) {
+ return new Response(null, { status: 404, statusText: "Not found" });
+ }
+
+ const fonts = fontData["--font-google-sans-code"];
+ const regularFontPath = getFontPathByWeight(fonts, 400);
+ const boldFontPath = getFontPathByWeight(fonts, 700);
+
+ if (regularFontPath === undefined || boldFontPath === undefined) {
+ throw new Error("Cannot find the font path.");
+ }
+
+ const [regularData, boldData] = await Promise.all([
+ fetch(experimental_getFontFileURL(regularFontPath, url)).then(res =>
+ res.arrayBuffer()
+ ),
+ fetch(experimental_getFontFileURL(boldFontPath, url)).then(res =>
+ res.arrayBuffer()
+ ),
+ ]);
+
+ const svg = await satori(
+ {
+ key: null,
+ type: "div",
+ props: {
+ style: {
+ background: "#fefbfb",
+ width: "100%",
+ height: "100%",
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "center",
+ },
+ children: [
+ {
+ type: "div",
+ props: {
+ style: {
+ position: "absolute",
+ top: "-1px",
+ right: "-1px",
+ boxSizing: "border-box",
+ border: "4px solid #000",
+ background: "#ecebeb",
+ opacity: "0.9",
+ borderRadius: "4px",
+ display: "flex",
+ justifyContent: "center",
+ margin: "2.5rem",
+ width: "88%",
+ height: "80%",
+ },
+ },
+ },
+ {
+ type: "div",
+ props: {
+ style: {
+ position: "relative",
+ boxSizing: "border-box",
+ border: "4px solid #000",
+ background: "#fefbfb",
+ borderRadius: "4px",
+ display: "flex",
+ justifyContent: "center",
+ margin: "2rem",
+ width: "88%",
+ height: "80%",
+ },
+ children: {
+ type: "div",
+ props: {
+ style: {
+ display: "flex",
+ flexDirection: "column",
+ justifyContent: "space-between",
+ margin: "20px",
+ width: "90%",
+ height: "90%",
+ },
+ children: [
+ {
+ type: "p",
+ props: {
+ style: {
+ fontSize: 72,
+ fontWeight: "bold",
+ margin: "17px 0 0",
+ maxHeight: "80%",
+ overflow: "hidden",
+ },
+ children: props.data.title,
+ },
+ },
+ {
+ type: "div",
+ props: {
+ style: {
+ display: "flex",
+ justifyContent: "space-between",
+ width: "100%",
+ marginBottom: "8px",
+ fontSize: 28,
+ },
+ children: [
+ {
+ type: "span",
+ props: {
+ children: [
+ "by ",
+ {
+ type: "span",
+ props: {
+ style: {
+ overflow: "hidden",
+ fontWeight: "bold",
+ },
+ children: props.data.author,
+ },
+ },
+ ],
+ },
+ },
+ {
+ type: "span",
+ props: {
+ style: { overflow: "hidden", fontWeight: "bold" },
+ children: config.site.title,
+ },
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ },
+ },
+ ],
+ },
+ },
+ {
+ width: 1200,
+ height: 630,
+ embedFont: true,
+ fonts: [
+ {
+ name: "Google Sans Code",
+ data: regularData,
+ weight: 400,
+ style: "normal",
+ },
+ {
+ name: "Google Sans Code",
+ data: boldData,
+ weight: 700,
+ style: "normal",
+ },
+ ],
+ }
+ );
+
+ const pngBuffer = await sharp(Buffer.from(svg)).png().toBuffer();
+
+ return new Response(new Uint8Array(pngBuffer), {
+ headers: { "Content-Type": "image/png" },
+ });
+};
diff --git a/src/pages/robots.txt.ts b/src/pages/robots.txt.ts
new file mode 100644
index 0000000..4edef8b
--- /dev/null
+++ b/src/pages/robots.txt.ts
@@ -0,0 +1,13 @@
+import type { APIRoute } from "astro";
+
+const getRobotsTxt = (sitemapURL: URL) => `
+User-agent: *
+Allow: /
+
+Sitemap: ${sitemapURL.href}
+`;
+
+export const GET: APIRoute = ({ site }) => {
+ const sitemapURL = new URL("sitemap-index.xml", site);
+ return new Response(getRobotsTxt(sitemapURL));
+};
diff --git a/src/pages/rss.xml.ts b/src/pages/rss.xml.ts
new file mode 100644
index 0000000..2c05e02
--- /dev/null
+++ b/src/pages/rss.xml.ts
@@ -0,0 +1,22 @@
+import rss from "@astrojs/rss";
+import { getCollection } from "astro:content";
+import { getSortedPosts } from "@/utils/getSortedPosts";
+import { getPostUrl } from "@/utils/getPostPaths";
+import config from "@/config";
+
+export async function GET() {
+ const posts = await getCollection("posts");
+ const sortedPosts = getSortedPosts(posts);
+
+ return rss({
+ title: config.site.title,
+ description: config.site.description,
+ site: config.site.url,
+ items: sortedPosts.map(({ data, id, filePath }) => ({
+ link: getPostUrl(id, filePath, config.site.lang),
+ title: data.title,
+ description: data.description,
+ pubDate: new Date(data.modDatetime ?? data.pubDatetime),
+ })),
+ });
+}
diff --git a/src/pages/search.astro b/src/pages/search.astro
new file mode 100644
index 0000000..6ec022d
--- /dev/null
+++ b/src/pages/search.astro
@@ -0,0 +1,174 @@
+---
+import { getRelativeLocaleUrl } from "astro:i18n";
+import "@pagefind/default-ui/css/ui.css";
+import Layout from "@/layouts/Layout.astro";
+import Header from "@/components/Header.astro";
+import Breadcrumb from "@/components/Breadcrumb.astro";
+import Main from "@/components/Main.astro";
+import Footer from "@/components/Footer.astro";
+import { getAssetPath } from "@/utils/withBase";
+import { useTranslations } from "@/i18n";
+import config from "@/config";
+
+const locale = Astro.currentLocale ?? config.site.lang;
+
+// Redirect to 404 page if `search` feature is not enabled
+const notFoundUrl = getRelativeLocaleUrl(locale, "404");
+if (config.features.search !== "pagefind" && notFoundUrl) {
+ return Astro.rewrite(notFoundUrl);
+}
+
+const backUrl = config.features.showBackButton
+ ? `${Astro.url.pathname}`
+ : getRelativeLocaleUrl(locale, "");
+
+const pagefindBundlePath = getAssetPath("pagefind/");
+
+const t = useTranslations(locale);
+---
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/tags/[tag]/[...page].astro b/src/pages/tags/[tag]/[...page].astro
new file mode 100644
index 0000000..9087f1f
--- /dev/null
+++ b/src/pages/tags/[tag]/[...page].astro
@@ -0,0 +1,57 @@
+---
+import { getCollection } from "astro:content";
+import type { GetStaticPathsOptions } from "astro";
+import Layout from "@/layouts/Layout.astro";
+import Header from "@/components/Header.astro";
+import Main from "@/components/Main.astro";
+import Breadcrumb from "@/components/Breadcrumb.astro";
+import Card from "@/components/Card.astro";
+import Footer from "@/components/Footer.astro";
+import Pagination from "@/components/Pagination.astro";
+import { getUniqueTags } from "@/utils/getUniqueTags";
+import { getSortedPosts } from "@/utils/getSortedPosts";
+import { slugifyAll } from "@/utils/slugify";
+import { useTranslations } from "@/i18n";
+import config from "@/config";
+
+export async function getStaticPaths({ paginate }: GetStaticPathsOptions) {
+ const posts = await getCollection("posts", ({ data }) => !data.draft);
+ const tags = getUniqueTags(posts);
+
+ return tags.flatMap(({ tag, tagName }) => {
+ const tagPosts = getSortedPosts(
+ posts.filter(({ data }) => slugifyAll(data.tags).includes(tag))
+ );
+
+ return paginate(tagPosts, {
+ params: { tag },
+ props: { tagName },
+ pageSize: config.posts.perPage,
+ });
+ });
+}
+
+const { page, tagName } = Astro.props;
+
+const locale = Astro.currentLocale ?? config.site.lang;
+const t = useTranslations(locale);
+---
+
+
+
+
+
+
+
+
+ {page.data.map(data => )}
+
+
+
+
+
+
diff --git a/src/pages/tags/index.astro b/src/pages/tags/index.astro
new file mode 100644
index 0000000..370bc87
--- /dev/null
+++ b/src/pages/tags/index.astro
@@ -0,0 +1,32 @@
+---
+import { getCollection } from "astro:content";
+import Layout from "@/layouts/Layout.astro";
+import Header from "@/components/Header.astro";
+import Breadcrumb from "@/components/Breadcrumb.astro";
+import Main from "@/components/Main.astro";
+import Footer from "@/components/Footer.astro";
+import Tag from "@/components/Tag.astro";
+import { getUniqueTags } from "@/utils/getUniqueTags";
+import { useTranslations } from "@/i18n";
+import config from "@/config";
+
+const posts = await getCollection("posts", ({ data }) => !data.draft);
+const tags = getUniqueTags(posts);
+
+const locale = Astro.currentLocale ?? config.site.lang;
+const t = useTranslations(locale);
+---
+
+
+
+
+
+
+
+
+ {tags.map(({ tag, tagName }) => )}
+
+
+
+
+
diff --git a/src/remark-collapse.d.ts b/src/remark-collapse.d.ts
new file mode 100644
index 0000000..5257ddb
--- /dev/null
+++ b/src/remark-collapse.d.ts
@@ -0,0 +1,2 @@
+// Deprecated with the package: see the note in astro.config.ts. Removed in v8.
+declare module "remark-collapse";
diff --git a/src/scripts/theme.ts b/src/scripts/theme.ts
new file mode 100644
index 0000000..9ee31c7
--- /dev/null
+++ b/src/scripts/theme.ts
@@ -0,0 +1,69 @@
+const THEME_KEY = "theme";
+const LIGHT = "light";
+const DARK = "dark";
+
+function getPreferredTheme(): string {
+ const stored = localStorage.getItem(THEME_KEY);
+ if (stored) return stored;
+ return window.matchMedia("(prefers-color-scheme: dark)").matches
+ ? DARK
+ : LIGHT;
+}
+
+// Reuse the value already set by the inline FOUC-prevention script if available.
+let themeValue: string =
+ (window as unknown as { __theme?: { value: string } }).__theme?.value ??
+ getPreferredTheme();
+
+function persist(): void {
+ localStorage.setItem(THEME_KEY, themeValue);
+ reflect();
+}
+
+function reflect(): void {
+ const root = document.firstElementChild;
+ root?.setAttribute("data-theme", themeValue);
+ root?.classList.toggle("dark", themeValue === DARK);
+ document.querySelector("#theme-btn")?.setAttribute("aria-label", themeValue);
+
+ // Fill with the computed background colour so
+ // Android's browser chrome matches the page background.
+ const bg = window.getComputedStyle(document.body).backgroundColor;
+ document
+ .querySelector("meta[name='theme-color']")
+ ?.setAttribute("content", bg);
+}
+
+function setup(): void {
+ reflect();
+ document.querySelector("#theme-btn")?.addEventListener("click", () => {
+ themeValue = themeValue === LIGHT ? DARK : LIGHT;
+ persist();
+ });
+}
+
+setup();
+
+// Re-run after View Transitions navigation.
+document.addEventListener("astro:after-swap", setup);
+
+// Carry the theme-color value across View Transitions to prevent the
+// Android navigation bar from flashing during page transitions.
+document.addEventListener("astro:before-swap", event => {
+ const color = document
+ .querySelector("meta[name='theme-color']")
+ ?.getAttribute("content");
+ if (color) {
+ (event as { newDocument: Document }).newDocument
+ .querySelector("meta[name='theme-color']")
+ ?.setAttribute("content", color);
+ }
+});
+
+// Sync with OS-level dark/light preference changes.
+window
+ .matchMedia("(prefers-color-scheme: dark)")
+ .addEventListener("change", ({ matches }) => {
+ themeValue = matches ? DARK : LIGHT;
+ persist();
+ });
diff --git a/src/styles/global.css b/src/styles/global.css
new file mode 100644
index 0000000..f4396bd
--- /dev/null
+++ b/src/styles/global.css
@@ -0,0 +1,39 @@
+@import "tailwindcss";
+@import "./theme.css";
+@import "./typography.css";
+@import "rehype-callouts/theme/obsidian";
+
+@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
+
+@layer base {
+ * {
+ @apply border-border outline-accent/75;
+ scrollbar-width: auto;
+ scrollbar-color: var(--color-muted) transparent;
+ }
+ a,
+ button,
+ #main-content {
+ @apply outline-accent outline-offset-1 focus-visible:no-underline focus-visible:outline-2 focus-visible:outline-dashed;
+ }
+ button:not(:disabled),
+ [role="button"]:not(:disabled) {
+ cursor: pointer;
+ }
+}
+
+@utility max-w-app {
+ @apply max-w-3xl;
+}
+
+@utility app-layout {
+ @apply max-w-app mx-auto w-full px-4;
+}
+
+@utility active-nav {
+ @apply underline decoration-wavy decoration-2 underline-offset-8;
+}
+
+:target {
+ scroll-margin-block: 1rem;
+}
diff --git a/src/styles/theme.css b/src/styles/theme.css
new file mode 100644
index 0000000..561be38
--- /dev/null
+++ b/src/styles/theme.css
@@ -0,0 +1,34 @@
+/* Register design tokens for Tailwind v4 */
+@theme inline {
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --color-accent: var(--accent);
+ --color-accent-foreground: var(--accent-foreground);
+ --color-muted: var(--muted);
+ --color-muted-foreground: var(--muted-foreground);
+ --color-border: var(--border);
+ --font-app: var(--font-google-sans-code);
+}
+
+/* Light theme values */
+:root,
+[data-theme="light"] {
+ --background: #fdfdfd;
+ --foreground: #282728;
+ --accent: #006cac;
+ --accent-foreground: #ffffff;
+ --muted: #e6e6e6;
+ --muted-foreground: #6b7280;
+ --border: #ece9e9;
+}
+
+/* Dark theme values */
+[data-theme="dark"] {
+ --background: #212737;
+ --foreground: #eaedf3;
+ --accent: #ff6b01;
+ --accent-foreground: #ffffff;
+ --muted: #343f60;
+ --muted-foreground: #afb9ca;
+ --border: #ab4b08;
+}
diff --git a/src/styles/typography.css b/src/styles/typography.css
new file mode 100644
index 0000000..7c8727a
--- /dev/null
+++ b/src/styles/typography.css
@@ -0,0 +1,146 @@
+@plugin "@tailwindcss/typography";
+
+@layer base {
+ /* ===== Override default Tailwind Typography styles ===== */
+ .app-prose {
+ @apply prose;
+
+ h1,
+ h2,
+ h3,
+ h4,
+ th {
+ @apply text-foreground mb-3;
+ }
+
+ h3 {
+ @apply italic;
+ }
+
+ p,
+ strong,
+ ol,
+ ul,
+ figcaption,
+ table,
+ code {
+ @apply text-foreground;
+ }
+
+ a {
+ @apply text-foreground hover:text-accent wrap-break-word decoration-dashed underline-offset-4 focus-visible:no-underline;
+ }
+
+ ul {
+ @apply overflow-x-clip;
+ }
+
+ li {
+ @apply marker:text-accent;
+ }
+
+ hr {
+ @apply border-border;
+ }
+
+ img {
+ @apply border-border mx-auto border;
+ }
+
+ img[role="button"] {
+ @apply cursor-zoom-in;
+
+ &:focus-visible {
+ @apply outline-2 outline-offset-2 outline-dashed;
+ }
+ }
+
+ figcaption {
+ @apply opacity-75;
+ }
+
+ table {
+ th,
+ td {
+ @apply border-border border p-2;
+ }
+
+ th {
+ @apply py-1.5;
+ }
+
+ th[align="center"],
+ td[align="center"] {
+ text-align: center;
+ }
+
+ th[align="right"],
+ td[align="right"] {
+ text-align: right;
+ }
+
+ th[align="left"],
+ td[align="left"] {
+ text-align: left;
+ }
+ }
+
+ .table-wrapper {
+ @apply outline-accent my-7 overflow-x-auto focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-dashed;
+
+ table {
+ @apply my-0;
+ }
+ }
+
+ code {
+ @apply bg-muted/75 text-foreground rounded p-1 wrap-break-word before:content-none after:content-none;
+ }
+
+ .astro-code code {
+ @apply flex-[1_0_100%] bg-inherit p-0;
+ }
+
+ blockquote {
+ @apply border-s-accent/80 wrap-break-word opacity-80;
+ }
+
+ details:not(.callout) {
+ @apply text-foreground inline-block cursor-pointer select-none [&_p]:hidden [&_ul]:my-0!;
+ }
+
+ summary {
+ @apply focus-visible:outline-accent focus-visible:no-underline focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-dashed;
+ }
+
+ pre {
+ @apply focus-visible:outline-accent focus-visible:border-transparent focus-visible:outline-2 focus-visible:outline-dashed;
+ }
+ }
+
+ /* ===== Code Blocks & Syntax Highlighting ===== */
+ .astro-code {
+ @apply outline-border flex border bg-(--shiki-light-bg) text-(--shiki-light) [&_span]:text-(--shiki-light);
+ }
+
+ html[data-theme="dark"] .astro-code {
+ @apply bg-(--shiki-dark-bg) text-(--shiki-dark) [&_span]:text-(--shiki-dark);
+ }
+
+ /* Styles for Shiki transformers */
+ /* https://shiki.style/packages/transformers */
+ .astro-code {
+ .line.diff.add {
+ @apply relative inline-block w-full bg-green-400/20 before:absolute before:-left-3 before:text-green-500 before:content-['+'];
+ }
+ .line.diff.remove {
+ @apply relative inline-block w-full bg-red-500/20 before:absolute before:-left-3 before:text-red-500 before:content-['-'];
+ }
+ .line.highlighted {
+ @apply inline-block w-full bg-slate-400/20;
+ }
+ .highlighted-word {
+ @apply border-border rounded-sm border px-0.5 py-px;
+ }
+ }
+}
diff --git a/src/types/config.ts b/src/types/config.ts
new file mode 100644
index 0000000..f5ff1ae
--- /dev/null
+++ b/src/types/config.ts
@@ -0,0 +1,143 @@
+interface SiteConfig {
+ /** Deployed URL of the site, e.g. "https://example.com" */
+ url: string;
+ /** Blog title shown in header and meta tags */
+ title: string;
+ /** Short description used in SEO meta and RSS feed */
+ description: string;
+ /** Default post author name */
+ author: string;
+ /** Author profile URL (used in structured data) */
+ profile?: string;
+ /** Fallback OG image filename in /public, e.g. "og.jpg" */
+ ogImage?: string;
+ /** HTML lang attribute, defaults to "en" */
+ lang?: string;
+ /** IANA timezone for post dates, e.g. "Asia/Bangkok" */
+ timezone?: string;
+ /** Text direction */
+ dir?: "ltr" | "rtl" | "auto";
+ /** Google Search Console verification meta tag value */
+ googleVerification?: string;
+}
+
+interface PostsConfig {
+ /** Posts per page on paginated listing pages */
+ perPage?: number;
+ /** Posts shown on the index/home page */
+ perIndex?: number;
+ /**
+ * @deprecated No longer has any effect. Publishing is day-granular: a post is
+ * listed from the start of its `pubDatetime` day in the site timezone, so
+ * there is no sub-day boundary left for a margin to cover. Safe to remove.
+ */
+ scheduledPostMargin?: number;
+}
+
+interface FeaturesConfig {
+ /** Enable light/dark mode toggle. Defaults to true. */
+ lightAndDarkMode?: boolean;
+ /**
+ * Generate dynamic OG images per post and provide `/og.png` when the static
+ * `public/{site.ogImage}` file is absent. When false, that file is required
+ * for the default layout OG image (build fails if missing).
+ */
+ dynamicOgImage?: boolean;
+ /** Show the /archives page and link it in nav. Defaults to true. */
+ showArchives?: boolean;
+ /** Show back button on post detail pages. Defaults to true. */
+ showBackButton?: boolean;
+ /** "Edit page" link shown on post detail pages. */
+ editPost?:
+ | {
+ enabled: true;
+ /** Base URL for the edit link, e.g. GitHub edit URL */
+ url: string;
+ }
+ | { enabled: false };
+ /**
+ * Search provider. "pagefind" ships in the base template.
+ * Set to false to disable search entirely.
+ */
+ search?: "pagefind" | false;
+ /**
+ * Table of contents on posts with 2+ h2/h3 headings: beside the post from
+ * 1280px, in the floating button below that. Defaults to true. Turn it off
+ * for one post with `hideToc: true` in its frontmatter.
+ */
+ toc?: boolean;
+}
+
+interface SocialLink {
+ /**
+ * Must match an SVG filename in src/assets/icons/socials/.
+ * e.g. "github" → src/assets/icons/socials/github.svg
+ */
+ name: string;
+ url: string;
+ /**
+ * Accessible label for the icon link (aria-label, title attribute).
+ * Auto-generated if omitted: "{site.title} on GitHub", "Send an email to {site.title}", etc.
+ * Override when the default wording doesn't fit.
+ */
+ linkTitle?: string;
+}
+
+interface ShareLink {
+ /**
+ * Must match an SVG filename in src/assets/icons/socials/.
+ * e.g. "facebook" → src/assets/icons/socials/facebook.svg
+ */
+ name: string;
+ /** Base share URL. The post URL will be appended as a query param. */
+ url: string;
+ /**
+ * Accessible label for the icon link (aria-label, title attribute).
+ * Auto-generated if omitted: "Share this post on Facebook", "Share this post via WhatsApp", etc.
+ * Override when the default wording doesn't fit.
+ */
+ linkTitle?: string;
+}
+
+interface AstroPaperConfig {
+ site: SiteConfig;
+ posts?: PostsConfig;
+ features?: FeaturesConfig;
+ /** Social profile links shown in header/footer */
+ socials?: SocialLink[];
+ /** Share links shown on post detail pages */
+ shareLinks?: ShareLink[];
+}
+
+type ResolvedSiteConfig = Required<
+ Pick<
+ SiteConfig,
+ | "url"
+ | "title"
+ | "description"
+ | "author"
+ | "lang"
+ | "timezone"
+ | "dir"
+ | "ogImage"
+ >
+> &
+ Pick;
+
+export interface ResolvedAstroPaperConfig {
+ site: ResolvedSiteConfig;
+ posts: Required;
+ features: Required;
+ socials: SocialLink[];
+ shareLinks: ShareLink[];
+}
+
+/**
+ * Type helper for astro-paper.config.ts.
+ * Provides full IntelliSense without any runtime overhead.
+ */
+export function defineAstroPaperConfig(
+ config: AstroPaperConfig
+): AstroPaperConfig {
+ return config;
+}
diff --git a/src/utils/buildTocTree.ts b/src/utils/buildTocTree.ts
new file mode 100644
index 0000000..ea66b33
--- /dev/null
+++ b/src/utils/buildTocTree.ts
@@ -0,0 +1,33 @@
+export type TocItem = {
+ depth: number;
+ slug: string;
+ text: string;
+ children: TocItem[];
+};
+
+type Heading = { depth: number; slug: string; text: string };
+
+/**
+ * Nests a flat heading list (h2 at the top). A skipped level (h2 → h4) nests
+ * under the closest shallower heading, and a post that starts at h3 puts it
+ * at the top level, so no heading order can break the build.
+ */
+export function buildTocTree(headings: Heading[], maxDepth = 3): TocItem[] {
+ const root: TocItem[] = [];
+ const stack: TocItem[] = [];
+
+ for (const { depth, slug, text } of headings) {
+ if (depth < 2 || depth > maxDepth) continue;
+ const item: TocItem = { depth, slug, text, children: [] };
+ while (stack.length && stack[stack.length - 1].depth >= depth) stack.pop();
+ (stack[stack.length - 1]?.children ?? root).push(item);
+ stack.push(item);
+ }
+
+ return root;
+}
+
+/** A TOC is worth showing from 2 headings in its depth range. */
+export function hasToc(headings: Heading[], maxDepth = 3) {
+ return headings.filter(h => h.depth >= 2 && h.depth <= maxDepth).length >= 2;
+}
diff --git a/src/utils/getFontPathByWeight.ts b/src/utils/getFontPathByWeight.ts
new file mode 100644
index 0000000..294439c
--- /dev/null
+++ b/src/utils/getFontPathByWeight.ts
@@ -0,0 +1,22 @@
+import type { FontData } from "astro:assets";
+
+export function getFontPathByWeight(
+ fonts: FontData[],
+ weight: number,
+ options?: {
+ style?: "normal" | "italic";
+ format?: string;
+ }
+): string | undefined {
+ const style = options?.style ?? "normal";
+ const format = options?.format ?? "truetype";
+
+ for (const font of fonts) {
+ if (font.weight === String(weight) && font.style === style) {
+ const src = font.src.find(file => file.format === format) ?? font.src[0];
+ if (src) return src.url;
+ }
+ }
+
+ return undefined;
+}
diff --git a/src/utils/getPostPaths.ts b/src/utils/getPostPaths.ts
new file mode 100644
index 0000000..5f5b486
--- /dev/null
+++ b/src/utils/getPostPaths.ts
@@ -0,0 +1,52 @@
+import { getRelativeLocaleUrl } from "astro:i18n";
+import { BLOG_PATH } from "@/content.config";
+import { slugifyStr } from "./slugify";
+import config from "@/config";
+
+function getPostPathSegments(filePath: string | undefined): string[] {
+ return (
+ filePath
+ ?.replace(BLOG_PATH, "")
+ .split("/")
+ .filter(path => path !== "")
+ .filter(path => !path.startsWith("_"))
+ .slice(0, -1)
+ .map(segment => slugifyStr(segment)) ?? []
+ );
+}
+
+function getIdSlug(id: string): string {
+ const postId = id.split("/");
+ return postId.length > 0 ? String(postId[postId.length - 1]) : id;
+}
+
+function getPostSlugPath(id: string, filePath: string | undefined): string {
+ const pathSegments = getPostPathSegments(filePath);
+ const slug = getIdSlug(id);
+ return pathSegments.length > 0
+ ? [...pathSegments, slug].join("/")
+ : String(slug);
+}
+
+/**
+ * Returns the slug-only path for use as a route param in `getStaticPaths`.
+ * No base prefix, no locale — Astro handles those at a higher level.
+ * e.g. `/examples/my-post`
+ */
+export function getPostSlug(id: string, filePath: string | undefined): string {
+ return `/${getPostSlugPath(id, filePath)}`;
+}
+
+/**
+ * Returns a fully navigable URL for use in `` and RSS links.
+ * Applies both locale routing and the configured Astro base via
+ * `getRelativeLocaleUrl`.
+ * e.g. `/posts/my-post` or `/en/posts/my-post`
+ */
+export function getPostUrl(
+ id: string,
+ filePath: string | undefined,
+ locale: string | undefined = config.site.lang
+): string {
+ return getRelativeLocaleUrl(locale, `posts/${getPostSlugPath(id, filePath)}`);
+}
diff --git a/src/utils/getSortedPosts.ts b/src/utils/getSortedPosts.ts
new file mode 100644
index 0000000..a716a39
--- /dev/null
+++ b/src/utils/getSortedPosts.ts
@@ -0,0 +1,22 @@
+import type { CollectionEntry } from "astro:content";
+import { postFilter } from "./postFilter";
+
+/**
+ * Returns posts that are eligible to be shown to users, sorted by “last updated”
+ * descending (uses `modDatetime` when present, otherwise `pubDatetime`).
+ *
+ * Note: filtering respects drafts and scheduled posts via `postFilter()`.
+ */
+export function getSortedPosts(posts: CollectionEntry<"posts">[]) {
+ return posts
+ .filter(postFilter)
+ .sort(
+ (a, b) =>
+ Math.floor(
+ new Date(b.data.modDatetime ?? b.data.pubDatetime).getTime() / 1000
+ ) -
+ Math.floor(
+ new Date(a.data.modDatetime ?? a.data.pubDatetime).getTime() / 1000
+ )
+ );
+}
diff --git a/src/utils/getUniqueTags.ts b/src/utils/getUniqueTags.ts
new file mode 100644
index 0000000..541ec77
--- /dev/null
+++ b/src/utils/getUniqueTags.ts
@@ -0,0 +1,28 @@
+import type { CollectionEntry } from "astro:content";
+import { postFilter } from "./postFilter";
+import { slugifyStr } from "./slugify";
+
+type Tag = {
+ tag: string;
+ tagName: string;
+};
+
+/**
+ * Builds a de-duplicated, sorted tag list from posts.
+ *
+ * - Drafts and scheduled posts are excluded via `postFilter()`
+ * - `tag` is the slug used in URLs; `tagName` is the original label for display
+ * - Uniqueness is based on the slug (so differently-cased labels collapse)
+ */
+export function getUniqueTags(posts: CollectionEntry<"posts">[]) {
+ const tags: Tag[] = posts
+ .filter(postFilter)
+ .flatMap(post => post.data.tags)
+ .map(tag => ({ tag: slugifyStr(tag), tagName: tag }))
+ .filter(
+ (value, index, self) =>
+ self.findIndex(tag => tag.tag === value.tag) === index
+ )
+ .sort((tagA, tagB) => tagA.tag.localeCompare(tagB.tag));
+ return tags;
+}
diff --git a/src/utils/postFilter.ts b/src/utils/postFilter.ts
new file mode 100644
index 0000000..26f17ad
--- /dev/null
+++ b/src/utils/postFilter.ts
@@ -0,0 +1,32 @@
+import type { CollectionEntry } from "astro:content";
+import dayjs from "dayjs";
+import utc from "dayjs/plugin/utc";
+import timezone from "dayjs/plugin/timezone";
+import config from "@/config";
+import { resolveTimezone } from "./resolveTimezone";
+
+dayjs.extend(utc);
+dayjs.extend(timezone);
+
+/**
+ * Determines whether a post is eligible to be listed/rendered.
+ *
+ * - Excludes drafts always
+ * - In production, excludes posts whose `pubDatetime` falls on a later calendar
+ * day than the build date, both read in the site (or per-post) timezone
+ * - In dev, always shows non-draft posts to make authoring easier
+ *
+ * Publishing is day-granular by design: a post becomes visible on its
+ * `pubDatetime` day, not at a time of day. `Datetime.astro` uses the same
+ * timezone, so the byline shows that day unless a later `modDatetime` is set.
+ */
+export function postFilter({ data }: CollectionEntry<"posts">) {
+ if (data.draft) return false;
+ if (import.meta.env.DEV) return true;
+
+ const tz = resolveTimezone(data.timezone ?? config.site.timezone);
+ const publishDay = dayjs(data.pubDatetime).tz(tz).startOf("day");
+ const buildDay = dayjs().tz(tz).startOf("day");
+
+ return !publishDay.isAfter(buildDay);
+}
diff --git a/src/utils/rehypeInlineToc.js b/src/utils/rehypeInlineToc.js
new file mode 100644
index 0000000..84a611e
--- /dev/null
+++ b/src/utils/rehypeInlineToc.js
@@ -0,0 +1,153 @@
+import { readFileSync, readdirSync } from "node:fs";
+import { basename, join } from "node:path";
+import { fileURLToPath } from "node:url";
+import { buildTocTree } from "./buildTocTree";
+
+const TOC_HEADING = /^(table[ -]of[ -])?contents?$|^toc$/i;
+const HEADING_TAG = /^h([1-6])$/;
+
+const textOf = node =>
+ node.type === "text"
+ ? node.value
+ : (node.children ?? []).map(textOf).join("");
+
+const list = items => ({
+ type: "element",
+ tagName: "ul",
+ properties: {},
+ children: items.map(item => ({
+ type: "element",
+ tagName: "li",
+ properties: {},
+ children: [
+ {
+ type: "element",
+ tagName: "a",
+ properties: { href: `#${item.slug}` },
+ children: [{ type: "text", value: item.text }],
+ },
+ ...(item.children.length ? [list(item.children)] : []),
+ ],
+ })),
+});
+
+/**
+ * Deprecated: posts get a layout TOC (`features.toc`); support for an
+ * author-written TOC heading ends in v8.
+ *
+ * Rehype plugin that replaces remark-toc + remark-collapse. The heading an
+ * author writes ("## Table of contents", same match as remark-toc) becomes a
+ * collapsed `details.toc-inline` listing the headings after it. The heading
+ * itself is removed, so it isn't in the page outline or in `headings`.
+ *
+ * Needs heading ids: put it after Astro's `rehypeHeadingIds`. unified runs a
+ * plugin only once, so that call is also where Astro collects `headings`;
+ * the removed heading is dropped from that list here.
+ */
+export const rehypeInlineToc =
+ ({ maxDepth = 6 } = {}) =>
+ (tree, file) => {
+ let target;
+ const headings = [];
+
+ const walk = parent => {
+ parent.children?.forEach((node, index) => {
+ const level =
+ node.type === "element" && HEADING_TAG.exec(node.tagName)?.[1];
+ if (!level) return walk(node);
+
+ const text = textOf(node).trim();
+ if (!target && TOC_HEADING.test(text)) {
+ target = { parent, index, text, id: node.properties?.id };
+ } else if (target && node.properties?.id) {
+ headings.push({
+ depth: Number(level),
+ slug: node.properties.id,
+ text,
+ });
+ }
+ });
+ };
+ walk(tree);
+
+ if (!target) return;
+ const items = buildTocTree(headings, maxDepth);
+
+ const collected = file.data.astro?.headings;
+ if (collected) {
+ file.data.astro.headings = collected.filter(h => h.slug !== target.id);
+ }
+
+ target.parent.children.splice(target.index, 1, {
+ type: "element",
+ tagName: "details",
+ properties: { className: ["toc-inline"], dataPagefindIgnore: true },
+ children: [
+ {
+ type: "element",
+ tagName: "summary",
+ properties: {},
+ children: [{ type: "text", value: target.text }],
+ },
+ {
+ type: "element",
+ tagName: "nav",
+ properties: { ariaLabel: target.text, dataTocNav: true },
+ children: items.length ? [list(items)] : [],
+ },
+ ],
+ });
+ };
+
+const POSTS_DIR = "src/content/posts/";
+const ATX_HEADING = /^ {0,3}#{1,6}\s+(.*?)(?:\s+#+)?\s*$/;
+const FENCE = /^ {0,3}(`{3,}|~{3,})(.*)$/;
+
+const usesTocHeading = source => {
+ let fence = "";
+ for (const line of source.split("\n")) {
+ const [, marks, rest] = FENCE.exec(line) ?? [];
+ if (fence) {
+ const closes =
+ marks?.[0] === fence[0] && marks.length >= fence.length && !rest.trim();
+ if (closes) fence = "";
+ } else if (marks) {
+ fence = marks;
+ } else if (TOC_HEADING.test(ATX_HEADING.exec(line)?.[1] ?? "")) {
+ return true;
+ }
+ }
+ return false;
+};
+
+/**
+ * Warns once per `astro dev` or `astro build` when posts still have an
+ * author-written TOC heading. It reads the post files directly, so the count
+ * doesn't depend on which posts get rendered.
+ */
+export const inlineTocDeprecation = () => ({
+ name: "astro-paper:inline-toc",
+ hooks: {
+ "astro:config:setup": ({ config, command, isRestart, logger }) => {
+ if (isRestart || (command !== "dev" && command !== "build")) return;
+ const dir = fileURLToPath(new URL(POSTS_DIR, config.root));
+ let files;
+ try {
+ files = readdirSync(dir, { recursive: true });
+ } catch {
+ return;
+ }
+ const posts = files.filter(
+ file =>
+ /\.mdx?$/.test(file) &&
+ !basename(file).startsWith("_") &&
+ usesTocHeading(readFileSync(join(dir, file), "utf8"))
+ );
+ if (!posts.length) return;
+ logger.warn(
+ `${posts.length} post(s) still have a "## Table of contents" heading (e.g. ${POSTS_DIR}${posts[0]}). ` +
+ "It's deprecated and stops working in v8: posts now get a table of contents from the layout (features.toc), so remove the heading."
+ );
+ },
+ },
+});
diff --git a/src/utils/rehypeTableWrapper.js b/src/utils/rehypeTableWrapper.js
new file mode 100644
index 0000000..4a99215
--- /dev/null
+++ b/src/utils/rehypeTableWrapper.js
@@ -0,0 +1,29 @@
+/**
+ * Rehype plugin that wraps each table in a focusable, horizontally scrollable
+ * `div.table-wrapper`, so wide tables scroll inside the post on small screens
+ * instead of widening the page. `tabindex="0"` lets keyboard users scroll it.
+ *
+ * Tables inside `` (MDX) are skipped: it has its own wrapper.
+ */
+export const rehypeTableWrapper = () => tree => {
+ const wrap = node => {
+ if (!node.children) return;
+ if (node.type === "mdxJsxFlowElement" && node.name === "ResponsiveTable")
+ return;
+
+ node.children = node.children.map(child => {
+ if (child.type === "element" && child.tagName === "table") {
+ return {
+ type: "element",
+ tagName: "div",
+ properties: { className: ["table-wrapper"], tabIndex: 0 },
+ children: [child],
+ };
+ }
+ wrap(child);
+ return child;
+ });
+ };
+
+ wrap(tree);
+};
diff --git a/src/utils/resolveDefaultOgImagePath.ts b/src/utils/resolveDefaultOgImagePath.ts
new file mode 100644
index 0000000..d0e62b4
--- /dev/null
+++ b/src/utils/resolveDefaultOgImagePath.ts
@@ -0,0 +1,51 @@
+import type { ResolvedAstroPaperConfig } from "@/types/config";
+import { getAssetPath } from "./withBase";
+
+const publicFiles = import.meta.glob("/public/*", {
+ eager: false,
+ query: "?url",
+});
+
+function existsInPublic(filename: string): boolean {
+ return `/public/${filename}` in publicFiles;
+}
+
+/**
+ * Resolves the absolute OG image path used for pages/posts.
+ *
+ * Security note: `site.ogImage` must be a single filename under `public/` to avoid
+ * path traversal or referencing arbitrary files.
+ *
+ * Behavior:
+ * - When `features.dynamicOgImage` is enabled, prefers `public/{site.ogImage}` when present,
+ * otherwise falls back to the generated `/og.png`.
+ * - When disabled, requires `public/{site.ogImage}` to exist.
+ */
+export function resolveDefaultOgImagePath(
+ config: ResolvedAstroPaperConfig
+): string {
+ const filename = config.site.ogImage;
+ if (
+ filename.includes("..") ||
+ filename.includes("/") ||
+ filename.includes("\\")
+ ) {
+ throw new Error(
+ `site.ogImage must be a single filename in public/ (e.g. "default-og.jpg"), got "${filename}"`
+ );
+ }
+
+ if (config.features.dynamicOgImage) {
+ return existsInPublic(filename)
+ ? getAssetPath(filename)
+ : getAssetPath("og.png");
+ }
+
+ if (!existsInPublic(filename)) {
+ throw new Error(
+ `AstroPaper: missing public/${filename}. Add that file, or set site.ogImage to an existing file under public/, or enable features.dynamicOgImage to fall back to /og.png.`
+ );
+ }
+
+ return getAssetPath(filename);
+}
diff --git a/src/utils/resolveTimezone.ts b/src/utils/resolveTimezone.ts
new file mode 100644
index 0000000..629af48
--- /dev/null
+++ b/src/utils/resolveTimezone.ts
@@ -0,0 +1,36 @@
+const warned = new Set();
+const valid = new Map();
+
+/**
+ * Returns `zone` if it is an IANA timezone this runtime recognises, otherwise
+ * falls back to UTC and warns once per bad value.
+ *
+ * `dayjs().tz()` throws a `RangeError` on an unrecognised name, which would abort
+ * the whole build from whichever component formatted the date first. A typo in
+ * `site.timezone` should degrade to UTC, not take the site down.
+ */
+export function resolveTimezone(zone: string | undefined): string {
+ if (!zone) return "UTC";
+
+ let ok = valid.get(zone);
+ if (ok === undefined) {
+ try {
+ new Intl.DateTimeFormat("en-US", { timeZone: zone });
+ ok = true;
+ } catch {
+ ok = false;
+ }
+ valid.set(zone, ok);
+ }
+ if (ok) return zone;
+
+ if (!warned.has(zone)) {
+ warned.add(zone);
+ // eslint-disable-next-line no-console
+ console.warn(
+ `[astro-paper] Unknown timezone "${zone}". Falling back to UTC. ` +
+ `Use an IANA name such as "Asia/Bangkok" or "America/New_York".`
+ );
+ }
+ return "UTC";
+}
diff --git a/src/utils/slugify.ts b/src/utils/slugify.ts
new file mode 100644
index 0000000..60afea8
--- /dev/null
+++ b/src/utils/slugify.ts
@@ -0,0 +1,18 @@
+import kebabcase from "lodash.kebabcase";
+import slugify from "slugify";
+
+const hasNonLatin = (str: string): boolean => /[^\x00-\x7F]/.test(str);
+
+/**
+ * Slugify a string using a hybrid approach:
+ * - Latin strings: slugify (e.g. "E2E Testing" → "e2e-testing")
+ * - Strings with non-Latin chars: lodash.kebabcase (preserves non-Latin chars)
+ */
+export const slugifyStr = (str: string): string => {
+ if (hasNonLatin(str)) {
+ return kebabcase(str);
+ }
+ return slugify(str, { lower: true });
+};
+
+export const slugifyAll = (arr: string[]) => arr.map(str => slugifyStr(str));
diff --git a/src/utils/toTransitionName.ts b/src/utils/toTransitionName.ts
new file mode 100644
index 0000000..f0333b6
--- /dev/null
+++ b/src/utils/toTransitionName.ts
@@ -0,0 +1,26 @@
+import { slugifyStr } from "./slugify";
+
+/**
+ * Produce a valid CSS for view-transition-name.
+ * CSS idents only allow [a-zA-Z0-9_-] plus Unicode U+00A0+.
+ * Non-ASCII chars are hex-encoded, ASCII special chars (:, /, etc.)
+ * are replaced with hyphens to keep the browser from ignoring the name.
+ */
+export const toTransitionName = (str: string): string => {
+ const base = slugifyStr(str.replaceAll(".", "-"));
+ let result = base
+ // encode non-ASCII chars (Chinese, Japanese, etc.)
+ .replace(
+ /[^\x00-\x7F]/gu,
+ c => "u" + c.codePointAt(0)!.toString(16).padStart(6, "0")
+ )
+ // replace any remaining invalid chars (colons, slashes, etc.)
+ .replace(/[^a-zA-Z0-9_-]/g, "-")
+ // collapse consecutive hyphens and trim
+ .replace(/-+/g, "-")
+ .replace(/^-+|-+$/g, "");
+ // CSS ident must not start with a digit
+ if (/^\d/.test(result)) result = "p-" + result;
+ if (!result) result = "post";
+ return result;
+};
diff --git a/src/utils/transformers/fileName.js b/src/utils/transformers/fileName.js
new file mode 100644
index 0000000..8ffd659
--- /dev/null
+++ b/src/utils/transformers/fileName.js
@@ -0,0 +1,69 @@
+/**
+ * CustomShiki transformer that adds file name labels to code blocks.
+ *
+ * This transformer looks for the `file="filename"` meta attribute in code blocks
+ * and creates a styled label showing the filename. It supports two different
+ * styling options and can optionally hide the green dot indicator.
+ *
+ * @param {Object} options - Configuration options for the transformer
+ * @param {string} [options.style="v2"] - The styling variant to use
+ * - `"v1"`: Tab-style with rounded top corners, positioned at top-left
+ * - `"v2"`: Badge-style with border, positioned at top-left with offset
+ * @param {boolean} [options.hideDot=false] - Whether to hide the green dot indicator
+ */
+export const transformerFileName = ({
+ style = "v2",
+ hideDot = false,
+} = {}) => ({
+ pre(node) {
+ // Add CSS custom property to the node
+ const fileNameOffset = style === "v1" ? "0.75rem" : "-0.75rem";
+ node.properties.style =
+ (node.properties.style || "") + `--file-name-offset: ${fileNameOffset};`;
+
+ const raw = this.options.meta?.__raw?.split(" ");
+
+ if (!raw) return;
+
+ const metaMap = new Map();
+
+ for (const item of raw) {
+ const [key, value] = item.split("=");
+ if (!key || !value) continue;
+ metaMap.set(key, value.replace(/["'`]/g, ""));
+ }
+
+ const file = metaMap.get("file");
+
+ if (!file) return;
+
+ // Add additional margin and top padding to code block
+ this.addClassToHast(
+ node,
+ `mt-8 pt-4.5 ${style === "v1" ? "rounded-tl-none" : ""}`
+ );
+
+ // Add file name to code block
+ node.children.push({
+ type: "element",
+ tagName: "span",
+ properties: {
+ class: [
+ "absolute py-1 text-foreground text-xs font-medium leading-4",
+ hideDot
+ ? "px-2"
+ : "pl-4 pr-2 before:inline-block before:size-1 before:bg-green-500 before:rounded-full before:absolute before:top-[45%] before:left-2",
+ style === "v1"
+ ? "left-0 -top-6 rounded-t-md border border-b-0 bg-muted/50"
+ : "left-2 top-(--file-name-offset) border rounded-md bg-background",
+ ],
+ },
+ children: [
+ {
+ type: "text",
+ value: file,
+ },
+ ],
+ });
+ },
+});
diff --git a/src/utils/withBase.ts b/src/utils/withBase.ts
new file mode 100644
index 0000000..62ad713
--- /dev/null
+++ b/src/utils/withBase.ts
@@ -0,0 +1,46 @@
+const base = import.meta.env.BASE_URL.replace(/\/+$/, "");
+const baseRoot = base === "" ? "/" : `${base}/`;
+
+/**
+ * Strip a locale prefix from a root-relative pathname.
+ * e.g. with locale "en": "/en/posts/foo" → "/posts/foo", "/en" → "/"
+ * Paths that don't start with the locale prefix are returned unchanged.
+ */
+export function stripLocale(pathname: string, locale: string): string {
+ const prefix = `/${locale}`;
+ if (pathname === prefix) return "/";
+ if (pathname.startsWith(`${prefix}/`)) return pathname.slice(prefix.length);
+ return pathname;
+}
+
+/**
+ * Strip the configured Astro `base` prefix from an absolute pathname.
+ * Returns a root-relative pathname.
+ */
+export function stripBase(pathname: string): string {
+ if (base === "") {
+ return pathname;
+ }
+ if (pathname === base) {
+ return "/";
+ }
+ if (pathname.startsWith(baseRoot)) {
+ const stripped = pathname.slice(base.length);
+ return stripped === "" ? "/" : stripped;
+ }
+ return pathname;
+}
+
+/**
+ * Prefix an asset/file path with the configured Astro `base`.
+ * Does not force a trailing slash for empty paths.
+ */
+export function getAssetPath(path: string): string {
+ // Strip leading slash to avoid double-slash when concatenating with baseRoot
+ const normalizedPath = path.replace(/^\/+/, "");
+
+ if (!normalizedPath) {
+ return base === "" ? "/" : base;
+ }
+ return baseRoot + normalizedPath;
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..62c3bd7
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "extends": "astro/tsconfigs/strict",
+ "include": [".astro/types.d.ts", "**/*"],
+ "exclude": ["dist", "public/pagefind"],
+ "compilerOptions": {
+ "paths": {
+ "@/*": ["./src/*"],
+ "@/astro-paper.config": ["./astro-paper.config"]
+ }
+ }
+}