From 009603319348542c34adb2ba1d830e688f06f0a7 Mon Sep 17 00:00:00 2001 From: Anirudh Oppiliappan Date: Wed, 15 Jul 2026 14:44:50 +0300 Subject: [PATCH] docs: rebuild with astro starlight Signed-off-by: Anirudh Oppiliappan --- docs/.gitignore | 3 + docs/DOCS.md | 2888 ------ docs/astro.config.mjs | 120 + docs/highlight.theme | 93 - docs/logo.html | 6 - docs/mode.html | 3 - docs/package-lock.json | 7820 +++++++++++++++++ docs/package.json | 23 + docs/postcss.config.cjs | 5 + docs/public/_redirects | 12 + docs/public/apple-touch-icon.png | Bin 0 -> 4666 bytes docs/public/favicon.svg | 21 + docs/scripts/gen-ascii-dolly.mjs | 34 + docs/scripts/gen-sidebar-icons.mjs | 75 + docs/search.html | 1 - docs/src/assets/logotype-dark.svg | 12 + docs/src/assets/logotype-light.svg | 12 + docs/src/components/FeatureCard.astro | 23 + docs/src/components/HeaderLinks.astro | 9 + docs/src/content.config.ts | 7 + docs/src/content/docs/contributing/guide.md | 136 + docs/src/content/docs/contributing/hacking.md | 169 + docs/src/content/docs/index.mdx | 144 + docs/src/content/docs/knots/index.md | 60 + docs/src/content/docs/knots/self-hosting.md | 375 + docs/src/content/docs/quick-start.md | 251 + docs/src/content/docs/reference/bobbin.md | 239 + docs/src/content/docs/reference/migrations.md | 138 + docs/src/content/docs/reference/webhooks.md | 215 + docs/src/content/docs/sites.md | 105 + .../src/content/docs/spindles/architecture.md | 37 + docs/src/content/docs/spindles/index.md | 65 + docs/src/content/docs/spindles/secrets.md | 302 + .../src/content/docs/spindles/self-hosting.md | 189 + docs/src/content/docs/spindles/workflows.md | 686 ++ docs/src/content/docs/troubleshooting.md | 58 + docs/src/content/docs/why-atproto.md | 101 + docs/src/styles/custom.css | 86 + docs/src/styles/sidebar-icons.css | 88 + docs/src/styles/tangled.css | 97 + docs/styles.css | 216 - docs/tailwind.config.cjs | 35 + docs/template.html | 175 - docs/toc.html | 4 - docs/tsconfig.json | 5 + 45 files changed, 11757 insertions(+), 3386 deletions(-) create mode 100644 docs/.gitignore delete mode 100644 docs/DOCS.md create mode 100644 docs/astro.config.mjs delete mode 100644 docs/highlight.theme delete mode 100644 docs/logo.html delete mode 100644 docs/mode.html create mode 100644 docs/package-lock.json create mode 100644 docs/package.json create mode 100644 docs/postcss.config.cjs create mode 100644 docs/public/_redirects create mode 100644 docs/public/apple-touch-icon.png create mode 100644 docs/public/favicon.svg create mode 100644 docs/scripts/gen-ascii-dolly.mjs create mode 100644 docs/scripts/gen-sidebar-icons.mjs delete mode 100644 docs/search.html create mode 100644 docs/src/assets/logotype-dark.svg create mode 100644 docs/src/assets/logotype-light.svg create mode 100644 docs/src/components/FeatureCard.astro create mode 100644 docs/src/components/HeaderLinks.astro create mode 100644 docs/src/content.config.ts create mode 100644 docs/src/content/docs/contributing/guide.md create mode 100644 docs/src/content/docs/contributing/hacking.md create mode 100644 docs/src/content/docs/index.mdx create mode 100644 docs/src/content/docs/knots/index.md create mode 100644 docs/src/content/docs/knots/self-hosting.md create mode 100644 docs/src/content/docs/quick-start.md create mode 100644 docs/src/content/docs/reference/bobbin.md create mode 100644 docs/src/content/docs/reference/migrations.md create mode 100644 docs/src/content/docs/reference/webhooks.md create mode 100644 docs/src/content/docs/sites.md create mode 100644 docs/src/content/docs/spindles/architecture.md create mode 100644 docs/src/content/docs/spindles/index.md create mode 100644 docs/src/content/docs/spindles/secrets.md create mode 100644 docs/src/content/docs/spindles/self-hosting.md create mode 100644 docs/src/content/docs/spindles/workflows.md create mode 100644 docs/src/content/docs/troubleshooting.md create mode 100644 docs/src/content/docs/why-atproto.md create mode 100644 docs/src/styles/custom.css create mode 100644 docs/src/styles/sidebar-icons.css create mode 100644 docs/src/styles/tangled.css delete mode 100644 docs/styles.css create mode 100644 docs/tailwind.config.cjs delete mode 100644 docs/template.html delete mode 100644 docs/toc.html create mode 100644 docs/tsconfig.json diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..ddce69b6 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +dist/ +.astro/ diff --git a/docs/DOCS.md b/docs/DOCS.md deleted file mode 100644 index abccdbbe..00000000 --- a/docs/DOCS.md +++ /dev/null @@ -1,2888 +0,0 @@ ---- -title: Tangled docs -author: The Tangled Contributors -date: 21 Sun, Dec 2025 -abstract: | - Tangled is a decentralized code hosting and collaboration - platform. Every component of Tangled is open-source and - self-hostable. [tangled.org](https://tangled.org) also - provides hosting and CI services that are free to use. - - There are several models for decentralized code - collaboration platforms, ranging from ActivityPub’s - (Forgejo) federated model, to Radicle’s entirely P2P model. - Our approach attempts to be the best of both worlds by - adopting the AT Protocol—a protocol for building decentralized - social applications with a central identity - - Our approach to this is the idea of “knots”. Knots are - lightweight, headless servers that enable users to host Git - repositories with ease. Knots are designed for either single - or multi-tenant use which is perfect for self-hosting on a - Raspberry Pi at home, or larger “community” servers. By - default, Tangled provides managed knots where you can host - your repositories for free. - - The appview at tangled.org acts as a consolidated "view" - into the whole network, allowing users to access, clone and - contribute to repositories hosted across different knots - seamlessly. ---- - -# Quick start guide - -## Login or sign up - -You can [login](https://tangled.org) by using your AT Protocol -account. If you are unclear on what that means, simply head -to the [signup](https://tangled.org/signup) page and create -an account. By doing so, you will be choosing Tangled as -your account provider (you will be granted a handle of the -form `user.tngl.sh`). - -In the AT Protocol network, users are free to choose their account -provider (known as a "Personal Data Service", or PDS), and -login to applications that support AT accounts. - -You can think of it as "one account for all of the atmosphere"! - -If you already have an AT account (you may have one if you -signed up to Bluesky, for example), you can login with the -same handle on Tangled (so just use `user.bsky.social` on -the login page). - -## Add an SSH key - -Once you are logged in, you can start creating repositories -and pushing code. Tangled supports pushing git repositories -over SSH. - -First, you'll need to generate an SSH key if you don't -already have one: - -```bash -ssh-keygen -t ed25519 -C "foo@bar.com" -``` - -When prompted, save the key to the default location -(`~/.ssh/id_ed25519`) and optionally set a passphrase. - -Copy your public key to your clipboard: - -```bash -# on X11 -cat ~/.ssh/id_ed25519.pub | xclip -sel c - -# on wayland -cat ~/.ssh/id_ed25519.pub | wl-copy - -# on macos -cat ~/.ssh/id_ed25519.pub | pbcopy -``` - -Now, navigate to 'Settings' -> 'Keys' and hit 'Add Key', -paste your public key, give it a descriptive name, and hit -save. - -## Create a repository - -Once your SSH key is added, create your first repository: - -1. Hit the green `+` icon on the topbar, and select - repository -2. Enter a repository name -3. Add a description -4. Choose a knotserver to host this repository on -5. Hit create - -Knots are self-hostable, lightweight Git servers that can -host your repository. Unlike traditional code forges, your -code can live on any server. Read the [Knots](TODO) section -for more. - -## Configure SSH - -To ensure Git uses the correct SSH key and connects smoothly -to Tangled, add this configuration to your `~/.ssh/config` -file: - -``` -Host tangled.org - Hostname tangled.org - User git - IdentityFile ~/.ssh/id_ed25519 - AddressFamily inet -``` - -This tells SSH to use your specific key when connecting to -Tangled and prevents authentication issues if you have -multiple SSH keys. - -Note that this configuration only works for knotservers that -are hosted by tangled.org. If you use a custom knot, refer -to the [Knots](TODO) section. - -## Push your first repository - -Initialize a new Git repository: - -```bash -mkdir my-project -cd my-project - -git init -echo "# My Project" > README.md -``` - -Add some content and push! - -```bash -git add README.md -git commit -m "Initial commit" -git remote add origin git@tangled.org:user.tngl.sh/my-project -git push -u origin main -``` - -That's it! Your code is now hosted on Tangled. - -## Migrating an existing repository - -Moving your repositories from GitHub, GitLab, Bitbucket, or -any other Git forge to Tangled is straightforward. You'll -simply change your repository's remote URL. At the moment, -Tangled does not have any tooling to migrate data such as -GitHub issues or pull requests. - -First, create a new repository on tangled.org as described -in the [Quick Start Guide](#create-a-repository). - -Navigate to your existing local repository: - -```bash -cd /path/to/your/existing/repo -``` - -You can inspect your existing Git remote like so: - -```bash -git remote -v -``` - -You'll see something like: - -```bash -origin git@github.com:username/my-project.git (fetch) -origin git@github.com:username/my-project.git (push) -``` - -Update the remote URL to point to tangled: - -```bash -git remote set-url origin git@tangled.org:user.tngl.sh/my-project -``` - -Verify the change: - -```bash -git remote -v -``` - -You should now see: - -```bash -origin git@tangled.org:user.tngl.sh/my-project (fetch) -origin git@tangled.org:user.tngl.sh/my-project (push) -``` - -Push all your branches and tags to Tangled: - -```bash -git push -u origin --all -git push -u origin --tags -``` - -Your repository is now migrated to Tangled! All commit -history, branches, and tags have been preserved. - -## Mirroring a repository to Tangled - -If you want to maintain your repository on multiple forges -simultaneously, for example, keeping your primary repository -on GitHub while mirroring to Tangled for backup or -redundancy, you can do so by adding [multiple remotes](https://git-scm.com/docs/git-push#_remotes). - -You can configure your local repository to push to both -Tangled and, say, GitHub. You may already have the following -setup: - -```bash -$ git remote -v -origin git@github.com:username/my-project.git (fetch) -origin git@github.com:username/my-project.git (push) -``` - -Now add Tangled as an additional push URL to the same -remote: - -```bash -git remote set-url --add --push origin git@tangled.org:user.tngl.sh/my-project -``` - -You also need to re-add the original URL as a push -destination (Git will now use the original URL to fetch only): - -```bash -git remote set-url --add --push origin git@github.com:username/my-project.git -``` - -Verify your configuration: - -```bash -$ git remote -v -origin git@github.com:username/my-project.git (fetch) -origin git@tangled.org:user.tngl.sh/my-project (push) -origin git@github.com:username/my-project.git (push) -``` - -Notice that there's one fetch URL (the primary remote) and -two push URLs. Now, whenever you push, Git will -automatically push to both remotes: - -```bash -git push origin main -``` - -This single command pushes your `main` branch to both GitHub -and Tangled simultaneously. - -To push all branches and tags: - -```bash -git push origin --all -git push origin --tags -``` - -If you prefer more control over which remote you push to, -you can maintain separate remotes: - -```bash -git remote add github git@github.com:username/my-project.git -git remote add tangled git@tangled.org:user.tngl.sh/my-project -``` - -Then push to each explicitly: - -```bash -git push github main -git push tangled main -``` - -# Hosting websites on Tangled - -You can serve static websites directly from your git repositories on -Tangled. If you've used GitHub Pages or Codeberg Pages, this should feel -familiar. - -## Overview - -Every user gets a sites domain. If you signed up through Tangled's own -PDS (`tngl.sh`), your sites domain is automatically -`.tngl.sh` no setup needed. Otherwise, you can claim a -`.tngl.io` domain from your settings. - -You can serve multiple sites per domain: - -- One **index site** served at the root of your domain (e.g. - `alice.tngl.sh`) -- Any number of **sub-path sites** served under the repository name - (e.g. `alice.tngl.sh/my-project`) - -## Claiming a domain - -If you don't have a `tngl.sh` handle, you need to claim a domain before -publishing sites: - -1. Go to **Settings → Sites** -2. Enter a subdomain (e.g. `alice` to claim `alice.tngl.io`) -3. Click **claim** - -You can only hold one domain at a time. Releasing a domain puts it in a -30-day cooldown before anyone else can claim it. - -## Configuring a site for a repository - -1. Navigate to your repository -2. Go to **Settings → Sites** -3. Choose a **branch** to deploy from -4. Set the **deploy directory** — the path within the repository - containing your `index.html`. Use `/` for the root, or a subdirectory - like `/docs` or `/public` -5. Choose the **site type**: - - **Index site** — served at the root of your domain (e.g. - `alice.tngl.sh`) - - **Sub-path site** — served under the repository name (e.g. - `alice.tngl.sh/my-project`) -6. Click **save** - -The site will be deployed automatically. You can see the status of your -previous deploys in the **Recent Deploys** section at the bottom of the -page. - -Sites are redeployed automatically on every push to the configured -branch. - -## Custom domains - -Tangled currently doesn't support custom domains for sites. This will be -added in a future update. - -## Deploy directory - -The deploy directory is the path within your repository that Tangled -serves as the site root. It must contain an `index.html`. - -| Deploy directory | Result | -|---|---| -| `/` | Serves the repository root | -| `/docs` | Serves the `docs/` subdirectory | -| `/public` | Serves the `public/` subdirectory | - -Directories are served with automatic `index.html` resolution -- a -request to `/about` will serve `/about/index.html` if it exists. - -## Site types - -| Type | URL | -|---|---| -| Index site | `alice.tngl.sh` | -| Sub-path site | `alice.tngl.sh/my-project` | - -Only one repository can be the index site for a given domain at a time. -If another repository already holds the index site, you will see a -notice in the settings and only the sub-path option will be available. - -## Deploy triggers - -A deployment is triggered automatically when: - -- You push to the configured branch -- You change the site configuration (branch, deploy directory, or site - type) - -## Disabling a site - -To stop serving a site, go to **Settings → Sites** in your repository -and click **Disable**. This removes the site configuration and stops -serving the site. The deployed files are also deleted from storage. - -Releasing your domain from **Settings → Sites** at the account level -will disable all sites associated with it and delete their files. - - -# Knot self-hosting guide - -So you want to run your own knot server? Great! Here are a few prerequisites: - -1. A server of some kind (a VPS, a Raspberry Pi, etc.). Preferably running a Linux distribution of some kind. -2. A (sub)domain name. People generally use `knot.example.com`. -3. A valid SSL certificate for your domain. - -## NixOS - -Refer to the [knot -module](https://tangled.org/tangled.org/core/blob/master/nix/modules/knot.nix) -for a full list of options. Sample configurations: - -- [The test VM](https://tangled.org/tangled.org/core/blob/master/nix/vm.nix#L85) -- [@pyrox.dev/nix](https://tangled.org/pyrox.dev/nix/blob/c2b644c214d278af12523618de952ee2eab1af3d/hosts/marvin/services/tangled.nix#L15-26) - -## Docker - -Refer to -[@tangled.org/knot-docker](https://tangled.org/@tangled.org/knot-docker). -Note that this is community maintained. - -## Manual setup - -First, clone this repository: - -``` -git clone https://tangled.org/@tangled.org/core -``` - -Then, build the `knot` CLI. This is the knot administration -and operation tool. For the purpose of this guide, we're -only concerned with these subcommands: - -- `knot server`: the main knot server process, typically - run as a supervised service -- `knot guard`: handles role-based access control for git - over SSH (you'll never have to run this yourself) -- `knot keys`: fetches SSH keys associated with your knot; - we'll use this to generate the SSH - `AuthorizedKeysCommand` - -``` -cd core -export CGO_ENABLED=1 -go build -o knot ./cmd/knot -``` - -Next, move the `knot` binary to a location owned by `root` -- -`/usr/local/bin/` is a good choice. Make sure the binary itself is also owned by `root`: - -``` -sudo mv knot /usr/local/bin/knot -sudo chown root:root /usr/local/bin/knot -``` - -This is necessary because SSH `AuthorizedKeysCommand` requires [really -specific permissions](https://stackoverflow.com/a/27638306). The -`AuthorizedKeysCommand` specifies a command that is run by `sshd` to -retrieve a user's public SSH keys dynamically for authentication. Let's -set that up. - -``` -sudo tee /etc/ssh/sshd_config.d/authorized_keys_command.conf < /home/git/motd -``` - -Note that you should add a newline at the end if setting a non-empty message -since the knot won't do this for you. - -## Secure Mode - -Secure Mode isolates each `git` subprocess to the repository it is -operating on, using two mechanisms: - -- **Linux Landlock** restricts the filesystem paths the subprocess - can access -- it can only read/write its own repository and the - system directories it needs to run. -- **UID isolation** runs each subprocess as a virtual UID assigned - to the repository owner, so that repositories belonging to - different owners are isolated from each other at the OS level - even if Landlock were somehow bypassed. - -Secure Mode requires: - -- Linux kernel >= 5.19 (Landlock V2). This is the minimum needed - for `git push` to work, because receive-pack's quarantine - migration uses cross-directory rename which requires the - Landlock `REFER` access right (added in V2). Kernels 5.13-5.18 - support Landlock V1 and clones will work, but pushes will fail - with cross-device link errors. On kernels without any Landlock - support (< 5.13), the sandbox call is a no-op: UID isolation - still applies but no filesystem restriction is enforced. -- `CAP_SETUID`, `CAP_SETGID`, and `CAP_CHOWN` available to the - knot process. The NixOS module grants these automatically; for - manual setups see the `setcap` step below. - -### NixOS - -Add `server.secureMode = true;` to your knot module configuration: - -```nix -services.tangled.knot = { - server.secureMode = true; - # ... other options -}; -``` - -The NixOS module handles everything else automatically: - -- Grants the required capabilities to the knot service via - `AmbientCapabilities` in the systemd unit. -- Installs a capability-bearing wrapper at - `/run/wrappers/bin/knot` via `security.wrappers`, so that - SSH-invoked git operations (pushes) also run under the correct - UID without requiring the service to run as root. -- Runs `knot migrate-isolation` at service start to chown - existing repositories to their virtual UIDs. - -### Manual setup - -**Step 1.** Grant the required capabilities to the knot binary. -This allows the knot process to switch to virtual UIDs at runtime -without running as root. You will need to repeat this step -whenever the binary is updated. - -``` -sudo setcap cap_setuid,cap_setgid,cap_chown+eip /usr/local/bin/knot -``` - -**Step 2.** Run the migration tool to assign virtual UIDs to all -existing repositories and set their filesystem permissions. This -must be run as root: - -``` -sudo knot migrate-isolation \ - --git-dir /home/git \ - --db /home/git/knotserver.db \ - --internal-api 127.0.0.1:5444 -``` - -You can re-run this at any time with `--force` to reapply -permissions (e.g. after a manual repair or after updating the -binary). - -**Step 2a.** Ensure the home directory is traversable by -non-group users. Git subprocesses run as virtual UIDs that are -not in the git group, and they need to resolve -`$HOME/.config/git/config` to load the global config: - -``` -sudo chmod o+x /home/git -``` - -This adds only the execute bit, not read -- the virtual UIDs can -traverse to known paths but cannot list directory contents. - -**Step 3.** Enable Secure Mode in your environment file: - -``` -KNOT_SERVER_SECURE_MODE=true -``` - -Or pass it as a flag: - -``` -knot server --secure-mode -``` - -**Step 4.** Regenerate the `AuthorizedKeysCommand` with the -`-secure-mode` flag. This causes `knot keys` to emit guard -command lines that include `-secure-mode`, so SSH pushes also -get UID isolation: - -``` -sudo tee /etc/ssh/sshd_config.d/authorized_keys_command.conf < **Note:** the server will refuse to start in Secure Mode if any -> repositories have not yet been isolation-migrated. Re-run -> `migrate-isolation` if you see this error. - -## Troubleshooting - -If you run your own knot, you may run into some of these -common issues. You can always join the -[IRC](https://web.libera.chat/#tangled) or -[Discord](https://chat.tangled.org/) if this section does -not help. - -### Unable to push - -If you are unable to push to your knot or repository: - -1. First, ensure that you have added your SSH public key to - your account -2. Check to see that your knot has synced the key by running - `knot keys` -3. Check to see if git is supplying the correct private key - when pushing: `GIT_SSH_COMMAND="ssh -v" git push ...` -4. Check to see if `sshd` on the knot is rejecting the push - for some reason: `journalctl -xeu ssh` (or `sshd`, - depending on your machine). These logs are unavailable if - using docker. -5. Check to see if the knot itself is rejecting the push, - depending on your setup, the logs might be in one of the - following paths: - - `/tmp/knotguard.log` - - `/home/git/log` - - `/home/git/guard.log` - -# Spindles - -## Pipelines - -Spindle workflows allow you to write CI/CD pipelines in a -simple format. They're located in the `.tangled/workflows` -directory at the root of your repository, and are defined -using YAML. - -A workflow has a set of common fields that apply no matter -which engine you pick: - -- [Trigger](#trigger): A **required** field that defines - when a workflow should be triggered. -- [Engine](#engine): A **required** field that defines which - engine a workflow should run on. -- [Clone options](#clone-options): An **optional** field - that defines how the repository should be cloned. -- [Environment](#environment): An **optional** field that - allows you to define environment variables. -- [Steps](#steps): An **optional** field that allows you to - define what steps should run in the workflow. - -On top of these, each engine has its own options for things -like dependencies and images. See [Engines](#engines) for -the per-engine fields. - -### Trigger - -The first thing to add to a workflow is the trigger, which -defines when a workflow runs. This is defined using a `when` -field, which takes in a list of conditions. Each condition -has the following fields: - -- `event`: This is a **required** field that defines when - your workflow should run. It's a list that can take one or - more of the following values: - - `push`: The workflow should run every time a commit is - pushed to the repository. - - `pull_request`: The workflow should run every time a - pull request is made or updated. - - `manual`: The workflow can be triggered manually. -- `branch`: Defines which branches the workflow should run - for. If used with the `push` event, commits to the - branch(es) listed here will trigger the workflow. If used - with the `pull_request` event, updates to pull requests - targeting the branch(es) listed here will trigger the - workflow. This field has no effect with the `manual` - event. Supports glob patterns using `*` and `**` (e.g., - `main`, `develop`, `release-*`). Either `branch` or `tag` - (or both) must be specified for `push` events. -- `tag`: Defines which tags the workflow should run for. - Only used with the `push` event - when tags matching the - pattern(s) listed here are pushed, the workflow will - trigger. This field has no effect with `pull_request` or - `manual` events. Supports glob patterns using `*` and `**` - (e.g., `v*`, `v1.*`, `release-**`). Either `branch` or - `tag` (or both) must be specified for `push` events. - -For example, if you'd like to define a workflow that runs -when commits are pushed to the `main` and `develop` -branches, or when pull requests that target the `main` -branch are updated, or manually, you can do so with: - -```yaml -when: - - event: ["push", "manual"] - branch: ["main", "develop"] - - event: ["pull_request"] - branch: ["main"] -``` - -You can also trigger workflows on tag pushes. For instance, -to run a deployment workflow when tags matching `v*` are -pushed: - -```yaml -when: - - event: ["push"] - tag: ["v*"] -``` - -You can even combine branch and tag patterns in a single -constraint (the workflow triggers if either matches): - -```yaml -when: - - event: ["push"] - branch: ["main", "release-*"] - tag: ["v*", "stable"] -``` - -To skip CI for a push, pass a Git push option: - -```sh -git push -o skip-ci -``` - -`ci-skip` is also accepted. - -### Engine - -Next is the engine on which the workflow should run, defined -using the **required** `engine` field. The currently -supported engines are: - -- `nixery`: This uses an instance of - [Nixery](https://nixery.dev) to run steps, which allows - you to add [dependencies](#dependencies) from - Nixpkgs (https://github.com/NixOS/nixpkgs). You can - search for packages on https://search.nixos.org, and - there's a pretty good chance the package(s) you're looking - for will be there. - See [Nixery engine](#nixery-engine). -- `microvm`: Runs the whole workflow inside its own - microVM. Has configuration features for NixOS images - that will let you enable services, do Docker-in-VM, etc. - See [microVM engine](#microvm-engine). - -Example: - -```yaml -engine: "nixery" -``` - -Each engine also adds its own workflow fields (dependencies, -images, services, and so on). These are documented under -[Engines](#engines). - -### Clone options - -When a workflow starts, the first step is to clone the -repository. You can customize this behavior using the -**optional** `clone` field. It has the following fields: - -- `skip`: Setting this to `true` will skip cloning the - repository. This can be useful if your workflow is doing - something that doesn't require anything from the - repository itself. This is `false` by default. -- `depth`: This sets the number of commits, or the "clone - depth", to fetch from the repository. For example, if you - set this to 2, the last 2 commits will be fetched. By - default, the depth is set to 1, meaning only the most - recent commit will be fetched, which is the commit that - triggered the workflow. -- `submodules`: If you use Git submodules - (https://git-scm.com/book/en/v2/Git-Tools-Submodules) - in your repository, setting this field to `true` will - recursively fetch all submodules. This is `false` by - default. - -The default settings are: - -```yaml -clone: - skip: false - depth: 1 - submodules: false -``` - -### Environment - -The `environment` field allows you define environment -variables that will be available throughout the entire -workflow. **Do not put secrets here, these environment -variables are visible to anyone viewing the repository. You -can add secrets for pipelines in your repository's -settings.** - -Example: - -```yaml -environment: - GOOS: "linux" - GOARCH: "arm64" - NODE_ENV: "production" - MY_ENV_VAR: "MY_ENV_VALUE" -``` - -By default, the following environment variables are set: - -- `CI` - Always set to `true` to indicate a CI environment -- `TANGLED_PIPELINE_ID` - The AT URI of the current pipeline -- `TANGLED_PIPELINE_KIND` - One of `push`, `pull_request` or - `manual` -- `TANGLED_REPO_KNOT` - The repository's knot hostname -- `TANGLED_REPO_DID` - The DID of the repository owner -- `TANGLED_REPO_NAME` - The name of the repository -- `TANGLED_REPO_DEFAULT_BRANCH` - The default branch of the - repository -- `TANGLED_REPO_URL` - The full URL to the repository - -These variables are only available when the pipeline is -triggered by a push: - -- `TANGLED_REF` - The full git reference (e.g., - `refs/heads/main` or `refs/tags/v1.0.0`) -- `TANGLED_REF_NAME` - The short name of the reference - (e.g., `main` or `v1.0.0`) -- `TANGLED_REF_TYPE` - The type of reference, either - `branch` or `tag` -- `TANGLED_SHA` - The commit SHA that triggered the pipeline -- `TANGLED_COMMIT_SHA` - Alias for `TANGLED_SHA` - -These variables are only available when the pipeline is -triggered by a pull request: - -- `TANGLED_PR_SOURCE_BRANCH` - The source branch of the pull - request -- `TANGLED_PR_TARGET_BRANCH` - The target branch of the pull - request -- `TANGLED_PR_SOURCE_SHA` - The commit SHA of the source - branch - -### Steps - -The `steps` field allows you to define what steps should run -in the workflow. It's a list of step objects, each with the -following fields: - -- `name`: This field allows you to give your step a name. - This name is visible in your workflow runs, and is used to - describe what the step is doing. -- `command`: This field allows you to define a command to - run in that step. The step is run in a Bash shell, and the - logs from the command will be visible in the pipelines - page on the Tangled website. Any dependencies you added in - your engine's section (see [Engines](#engines)) will be - available to use here. -- `environment`: Similar to the global - [environment](#environment) config, this **optional** - field is a key-value map that allows you to set - environment variables for the step. **Do not put secrets - here, these environment variables are visible to anyone - viewing the repository. You can add secrets for pipelines - in your repository's settings.** - -Example: - -```yaml -steps: - - name: "Build backend" - command: "go build" - environment: - GOOS: "darwin" - GOARCH: "arm64" - - name: "Build frontend" - command: "npm run build" - environment: - NODE_ENV: "production" -``` - -## Engines - -The common fields above apply to every workflow. Each engine -then adds its own fields on top. Pick an engine with the -[`engine`](#engine) field and use the matching section below. - -### Nixery engine - -#### Dependencies - -When you're running a workflow you'll usually need additional -dependencies. The `dependencies` field lets you define which -dependencies to get, and from where. It's a key-value map, -with the key being the registry to fetch dependencies from, -and the value being the list of dependencies to fetch. - -The registry URL syntax can be found [on the nix -manual](https://nix.dev/manual/nix/2.18/command-ref/new-cli/nix3-registry-add). - -Say you want to fetch Node.js and Go from `nixpkgs`, and a -package called `my_pkg` you've made from your own registry -at your repository at -`https://tangled.org/@example.com/my_pkg`. You can define -those dependencies like so: - -```yaml -dependencies: - # nixpkgs - nixpkgs: - - nodejs - - go - # unstable - nixpkgs/nixpkgs-unstable: - - bun - # custom registry - git+https://tangled.org/@example.com/my_pkg: - - my_pkg -``` - -Now these dependencies are available to use in your -workflow! - -#### Complete nixery workflow - -```yaml -# .tangled/workflows/build.yml - -when: - - event: ["push", "manual"] - branch: ["main", "develop"] - - event: ["pull_request"] - branch: ["main"] - -engine: "nixery" - -# using the default values -clone: - skip: false - depth: 1 - submodules: false - -dependencies: - # nixpkgs - nixpkgs: - - nodejs - - go - # custom registry - git+https://tangled.org/@example.com/my_pkg: - - my_pkg - -environment: - GOOS: "linux" - GOARCH: "arm64" - NODE_ENV: "production" - MY_ENV_VAR: "MY_ENV_VALUE" - -steps: - - name: "Build backend" - command: "go build" - environment: - GOOS: "darwin" - GOARCH: "arm64" - - name: "Build frontend" - command: "npm run build" - environment: - NODE_ENV: "production" -``` - -If you want another example of a workflow, you can look at -the one [Tangled uses to build the -project](https://tangled.org/@tangled.org/core/blob/master/.tangled/workflows/build.yml). - -### microVM engine - -#### Image - -A workflow picks the image to boot with the top-level `image` -field: - -```yaml -engine: microvm -image: nixos -``` - -There are two flavours of images: - -- **NixOS images** (e.g. `nixos`): the whole guest is built - with Nix, so you can configure it from the workflow file - itself. The `dependencies`, `services`, `virtualisation`, - `registry` and `caches` fields below are all understood - here, and the guest builds and activates that configuration - before any of your steps run. -- **Non-NixOS images** (e.g. `alpine`): there's no NixOS to - configure, so the workflow-level config fields above have - no effect. You still get a full machine to run steps in. - -The available image names depend on what the spindle operator -has installed. `nixos` and `alpine` are examples. If `image` -is omitted, the spindle's configured default image is used. - -#### Dependencies - -On the microVM engine, `dependencies` is a flat list of -packages that are made available to every step. This field -only applies to **NixOS images**; for other images you can -use the package manager included in a step. - -The guest builds a [`nix develop`](https://nix.dev/manual/nix/2.18/command-ref/new-cli/nix3-develop)-style -devshell from your dependencies and uses it for each step, -so you can, for example, add `pkg-config` and `openssl` and -have the `openssl-sys` crate while compiling a Rust project -just work. - -A bare name like `go` is looked up in nixpkgs. You can also -point at any flake with the `flakeref#attr` syntax, so -`github:nixos/nixpkgs#hello` pulls `hello` straight out of -that flake. - -```yaml -dependencies: - - go - - github:nixos/nixpkgs#hello -``` - -#### Registry - -The `registry` field remaps flake references, the same way -`nix registry` does. This lets you pin or alias the flakes -used by `dependencies`. - -For example, pin `nixpkgs` to `nixos-unstable` so that the -bare `go` above resolves from unstable, and alias your own -flake so you can use `myflake#tool` in `dependencies`: - -```yaml -registry: - nixpkgs: github:nixos/nixpkgs/nixos-unstable - myflake: github:me/x -``` - -#### Caches - -The `caches` field is a map of Nix binary cache URL to its -trusted public key. These are fed into the spindle's read -proxy, so the guest can substitute prebuilt paths from them -instead of building everything from scratch. - -```yaml -caches: - https://nix-community.cachix.org: "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" -``` - -#### Services and virtualisation - -The `services` and `virtualisation` fields are passed straight -through to NixOS. Anything you could write under -`services.*` or `virtualisation.*` in a NixOS configuration, -you can write here, and it's brought up before any of your -steps run. - -As a convenience, `true` works as shorthand for -`.enable = true` anywhere an `enable` option exists (e.g. -`virtualisation.docker: true`). - -```yaml -services: - postgresql: - enable: true - ensureDatabases: ["spindle-workflow"] - ensureUsers: - - name: spindle-workflow - ensureDBOwnership: true - -virtualisation: - docker: true -``` - -#### Recipes - -##### Lint, test and build a Node project - -```yaml -when: - - event: ["push", "pull_request"] - branch: ["main"] - -engine: microvm -image: nixos - -dependencies: - - pnpm - -steps: - - name: "Install dependencies" - command: pnpm install --frozen-lockfile - - name: "Lint and test" - command: | - pnpm run lint - pnpm test - - name: "Build" - command: pnpm run build -``` - -##### Check formatting - -```yaml -when: - - event: ["push", "pull_request"] - branch: ["main"] - -engine: microvm -image: alpine # slimmer image for checking the formatting - -steps: - - name: "Install go" - command: apk add go - - name: "Check formatting" - command: test -z $(gofmt -l .) -``` - -##### Build a Rust project that links OpenSSL - -```yaml -when: - - event: ["push", "pull_request"] - branch: ["main"] - -engine: microvm -image: nixos - -dependencies: - - gcc - - cargo - - rustc - - clippy - - rustfmt - - pkg-config # exports PKG_CONFIG_PATH for the libraries below - - openssl # the C library + headers openssl-sys links against - -steps: - - name: "Check formatting" - command: cargo fmt --check - - name: "Clippy" - command: cargo clippy --all-targets -- -D warnings - - name: "Test" - command: cargo test --all - - name: "Release build" - command: cargo build --release -``` - -##### Run migrations and integration tests against PostgreSQL - -```yaml -when: - - event: ["push", "pull_request"] - branch: ["main"] - -engine: microvm -image: nixos - -environment: - DATABASE_URL: "postgresql:///spindle-workflow?host=/run/postgresql" - -dependencies: - - gcc - - cargo - - rustc - - pkg-config - - openssl - - sqlx-cli - -services: - postgresql: - enable: true - # has to be same name as the user for peer auth to work automatically - ensureDatabases: ["spindle-workflow"] - ensureUsers: - - name: spindle-workflow - ensureDBOwnership: true - -steps: - - name: "Run migrations" - command: sqlx migrate run - - name: "Integration tests" - command: cargo test --all -``` - -##### Build and push a Docker image on tag - -```yaml -when: - - event: ["push"] - tag: ["v*"] - -engine: microvm -image: nixos - -virtualisation: - docker: true - -steps: - - name: "Build and push to ghcr.io" - command: | - set -euo pipefail - - echo "$REGISTRY_TOKEN" | docker login ghcr.io -u "$REGISTRY_USER" --password-stdin - image="ghcr.io/$REGISTRY_USER/myapp:$TANGLED_REF_NAME" - - docker build -t "$image" -t "ghcr.io/$REGISTRY_USER/myapp:latest" . - docker push "$image" - docker push "ghcr.io/$REGISTRY_USER/myapp:latest" -``` - -##### Deploy to Cloudflare Workers on tag - -```yaml -# .tangled/workflows/deploy.yml -when: - - event: ["push"] - tag: ["v*"] - -engine: microvm -image: nixos - -dependencies: - - pnpm - -steps: - - name: "Install dependencies" - command: pnpm install --frozen-lockfile - - name: "Deploy worker" - # `wrangler` picks up `CLOUDFLARE_API_TOKEN` from the env. - # set it under **Settings → Secrets**. - command: pnpm exec wrangler deploy -``` - -##### Publish a release artifact - -```yaml -when: - - event: ["push"] - tag: ["v*"] # trigger on versions - -engine: microvm -image: nixos - -dependencies: - - go - -steps: - - name: "Build release binary" - command: | - mkdir -p dist - CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o dist/myapp ./cmd/myapp - - - name: "Publish artifact record" - command: | - set -euo pipefail - # change this if you're not on `tngl.sh` - PDS="https://tngl.sh" - # also update this to your handle or did - ATP_IDENTIFIER="user.tngl.sh" - ARTIFACT_PATH="dist/myapp" - ARTIFACT_NAME="myapp" - - # set `ATP_APP_PASSWORD` under **Settings → Secrets** - session=$(curl -fsS -X POST "$PDS/xrpc/com.atproto.server.createSession" \ - -H "Content-Type: application/json" \ - -d "{\"identifier\":\"$ATP_IDENTIFIER\",\"password\":\"$ATP_APP_PASSWORD\"}") - jwt=$(echo "$session" | jq -r .accessJwt) - did=$(echo "$session" | jq -r .did) - - # upload the binary as a blob - blob=$(curl -fsS -X POST "$PDS/xrpc/com.atproto.repo.uploadBlob" \ - -H "Authorization: Bearer $jwt" \ - -H "Content-Type: application/octet-stream" \ - --data-binary @"$ARTIFACT_PATH") - - # note that this requires an annotated tag (`git tag -a v1.0.0 -m ...`) - tag_hash=$(git rev-parse "$TANGLED_REF_NAME^{tag}") - tag_bytes=$(printf '%s' "$tag_hash" | xxd -r -p | base64 | tr -d '=') - - # the sh.tangled.repo.artifact record for your artifact - record=$(jq -n \ - --arg did "$did" \ - --arg tag "$tag_bytes" \ - --arg name "$ARTIFACT_NAME" \ - --arg repo "$TANGLED_REPO_URL" \ - --arg created "$(date -Iseconds)" \ - --argjson blob "$(echo "$blob" | jq .blob)" '{ - repo: $did, - collection: "sh.tangled.repo.artifact", - validate: false, - record: { - "$type": "sh.tangled.repo.artifact", - tag: {"$bytes": $tag}, - name: $name, - repo: $repo, - artifact: $blob, - createdAt: $created - } - }') - - # create the record on the PDS - curl -fsS -X POST "$PDS/xrpc/com.atproto.repo.createRecord" \ - -H "Authorization: Bearer $jwt" \ - -H "Content-Type: application/json" \ - -d "$record" -``` - -## Self-hosting guide - -### Prerequisites - -- Go -- For the **nixery** engine: Docker (or Podman with Docker - compatibility enabled). -- For the **microVM** engine: a Linux host with KVM, plus the - microVM host dependencies described in [Running microVM - workflows](#running-microvm-workflows). - -### Configuration - -Spindle is configured using environment variables. The following environment variables are available: - -- `SPINDLE_SERVER_LISTEN_ADDR`: The address the server listens on (default: `"0.0.0.0:6555"`). -- `SPINDLE_SERVER_DB_PATH`: The path to the SQLite database file (default: `"spindle.db"`). -- `SPINDLE_SERVER_HOSTNAME`: The hostname of the server (required). -- `SPINDLE_SERVER_JETSTREAM_ENDPOINT`: The endpoint of the Jetstream server (default: `"wss://jetstream1.us-west.bsky.network/subscribe"`). -- `SPINDLE_SERVER_DEV`: A boolean indicating whether the server is running in development mode (default: `false`). -- `SPINDLE_SERVER_OWNER`: The DID of the owner (required). -- `SPINDLE_SERVER_LOG_DIR`: The directory to store workflow logs (default: `"/var/log/spindle"`). -- `SPINDLE_SERVER_DOCKER_SOCKET`: Path to Docker socket to expose to invoked Spindle containers (default: `""`). -- `SPINDLE_PIPELINES_NIXERY`: The Nixery URL (default: `"nixery.tangled.sh"`). -- `SPINDLE_PIPELINES_WORKFLOW_TIMEOUT`: The default workflow timeout (default: `"5m"`). - -For the microVM engine, the following are also available -(prefix `SPINDLE_MICROVM_PIPELINES_`): - -- `SPINDLE_MICROVM_PIPELINES_IMAGE_DIR`: Directory containing - microVM images (**required** to use the engine). See - [Running microVM workflows](#running-microvm-workflows). -- `SPINDLE_MICROVM_PIPELINES_DEFAULT_IMAGE`: Image used when a - workflow doesn't set `image` (default: `"nixos-x86_64"`). -- `SPINDLE_MICROVM_PIPELINES_OVERLAY_DIR`: Where per-workflow - temporary disks are created (default: the system temp dir). -- `SPINDLE_MICROVM_PIPELINES_ENABLE_KVM`: Use KVM hardware - acceleration (default: `true`). Without KVM, guests fall - back to slow software emulation. -- `SPINDLE_MICROVM_PIPELINES_WORKFLOW_TIMEOUT`: Default - workflow timeout (default: `"5m"`). - -Optional resource limits (a value of `0` disables that -limit). The limits cap usage across all running microVM -workflows: - -- `SPINDLE_MICROVM_PIPELINES_MAX_TOTAL_MEMORY_MIB` -- `SPINDLE_MICROVM_PIPELINES_MAX_TOTAL_VCPUS` -- `SPINDLE_MICROVM_PIPELINES_MAX_TOTAL_DISK_MIB` - -Optional cgroup enforcement: - -- `SPINDLE_MICROVM_PIPELINES_ENABLE_CGROUPS`: Place each - workflow's QEMU and slirp4netns in a per-workflow cgroup= - (default: `false`). -- `SPINDLE_MICROVM_PIPELINES_CGROUP_PARENT`: Parent cgroup; - `self` resolves the spindle service's own cgroup (default: - `"self"`). -- `SPINDLE_MICROVM_PIPELINES_CGROUP_PIDS_MAX`: Max processes - per workflow cgroup (default: `4096`). -- `SPINDLE_MICROVM_PIPELINES_CGROUP_SWAP_MAX_MIB`: Max swap - per workflow cgroup (default: `0`, no swap). -- `SPINDLE_MICROVM_PIPELINES_CGROUP_SUPERVISOR_MEMORY_MIN_MIB`: - Memory protected for spindle itself so it isn't OOM-killed - before the workflows (default: `512`). - -To push paths built inside microVMs back to a shared Nix -cache (and read from it), configure the cache (prefix -`SPINDLE_NIX_CACHE_`): - -- `SPINDLE_NIX_CACHE_READ_URLS`: Comma-separated binary cache - URLs the guest reads from. -- `SPINDLE_NIX_CACHE_TRUSTED_PUBLIC_KEYS`: Comma-separated - trusted public keys for those caches. -- `SPINDLE_NIX_CACHE_UPLOAD_URL`: Cache URL that paths built - in the guest are uploaded to. - -### Running spindle - -1. **Set the environment variables.** For example: - - ```shell - export SPINDLE_SERVER_HOSTNAME="your-hostname" - export SPINDLE_SERVER_OWNER="your-did" - ``` - -2. **Build the Spindle binary.** - - ```shell - cd core - go mod download - go build -o cmd/spindle/spindle cmd/spindle/main.go - ``` - -3. **Create the log directory.** - - ```shell - sudo mkdir -p /var/log/spindle - sudo chown $USER:$USER -R /var/log/spindle - ``` - -4. **Run the Spindle binary.** - - ```shell - ./cmd/spindle/spindle - ``` - -Spindle will now start, connect to the Jetstream server, and begin processing pipelines. - -### Running microVM workflows - -The microVM engine needs a few extra things on the host, and -it needs images to boot. - -#### Host dependencies - -microVM workflows depend on a handful of host tools and -devices. spindle checks for the ones an image needs right -before it launches, so a missing dependency surfaces as a -clear error. You'll need: - -- `qemu`: the runner. The QEMU binary for the image's arch - must be present (e.g. `qemu-system-x86_64`). -- `mkfs.ext4` (from `e2fsprogs`): to format the per-workflow - writable volumes. -- [`slirp4netns`](https://github.com/rootless-containers/slirp4netns#install), - `ip` (from `iproute2`), `mount` and `unshare` (from `util-linux`): - used to sandbox guest networking. -- `/dev/kvm`: for hardware acceleration (unless you disable - KVM with `SPINDLE_MICROVM_PIPELINES_ENABLE_KVM=false`). -- `/dev/vhost-vsock`: used by QEMU to enable guest-to-host vsock - communication. -- `/dev/vsock`: used by spindle on the host to listen on vsock ports - and accept guest agent connections. -- `/dev/net/tun`: required by `slirp4netns` to set up tap devices - for sandboxed guest networking. - -On NixOS, the [spindle -module](https://tangled.org/tangled.org/core/blob/master/nix/modules/spindle.nix) -puts `qemu`, `e2fsprogs`, `slirp4netns`, `iproute2` and -`util-linux` on the service's `PATH` for you. - -#### Container virtualization - -Running `spindle` inside a container (e.g., Docker, Podman, or LXD) requires passing host device nodes into the container and granting the runtime additional privileges. Because spindle uses nested namespaces (`unshare`) and helper tools (`slirp4netns`), the container configuration will require: - -- `/dev/vsock`, `/dev/vhost-vsock`, `/dev/kvm`, and `/dev/net/tun` mapped from the host. -- `NET_ADMIN` and `SYS_ADMIN` capabilities to manage network namespaces and mounts inside the container. -- Relaxed seccomp filters (e.g., `seccomp=unconfined`) and SELinux/AppArmor containment if they restrict namespace creation or device access. - -#### Building images - -Images are built with Nix. The flake exposes packages for the -two stock images (use the `-tarball` prefixed ones for a gzipped -tarball you can copy to another host): - -```shell -# a NixOS image -nix build .#spindle-nixos-image -# an Alpine image -nix build .#spindle-alpine-image -``` - -#### Installing images - -Spindle looks for images in -`SPINDLE_MICROVM_PIPELINES_IMAGE_DIR`. An image is resolved by -the name a workflow puts in its `image` field, matched -literally against what's on disk: - -1. a directory `/` containing a `spec.json` (next to the - kernel/initrd/store-disk), or -2. a flat `.json` self-contained spec. - -Resolution depends only on the name and what's on disk, never -on the host doing the resolving, so the same workflow resolves -to the same image on every spindle. If you keep multiple -arches side by side, you can name them `-` (e.g. -`nixos-x86_64`, `alpine-aarch64`); the suffix is just part of -the name. To make a name like `nixos` work if you are hosting -multiple arches, you can use symlinks. - -On NixOS, you'll most likely want to use `systemd.tmpfiles.rules` -to set these up declaratively. - -## Architecture - -Spindle is a small CI runner service. Here's a high-level overview of how it operates: - -- Listens for [`sh.tangled.spindle.member`](/lexicons/spindle/member.json) and - [`sh.tangled.repo`](/lexicons/repo.json) records on the Jetstream. -- When a new repo record comes through (typically when you add a spindle to a - repo from the settings), spindle then resolves the underlying knot and - subscribes to repo events (see: - [`sh.tangled.pipeline`](/lexicons/pipeline.json)). -- The spindle engine then handles execution of the pipeline, with results and - logs beamed on the spindle event stream over WebSocket - -### The engines - -Spindle has two execution backends, picked per-workflow with -the [`engine`](#engine) field: - -- **nixery**: executes each step in a fresh Docker container - (Podman works too, if Docker compatibility is enabled so - that `/run/docker.sock` is created), with state persisted - across steps within the `/tangled/workspace` directory. The - base image for the container is constructed on the fly using - [Nixery](https://nixery.dev), which is/rhandy for caching - layers for frequently used packages. -- **microvm**: runs the whole workflow inside its own - microVM, supporting different images, with extra - configuration for NixOS images (e.g. services in workflow file) - See the [engine - README](https://tangled.org/tangled.org/core/blob/master/spindle/engines/microvm/README.md) - for the architecture in depth. - -The pipeline manifest is [specified here](https://docs.tangled.org/spindles.html#pipelines). - -## Secrets with openbao - -This document covers setting up spindle to use OpenBao for secrets -management via OpenBao Proxy instead of the default SQLite backend. - -### Overview - -Spindle now uses OpenBao Proxy for secrets management. The proxy handles -authentication automatically using AppRole credentials, while spindle -connects to the local proxy instead of directly to the OpenBao server. - -This approach provides better security, automatic token renewal, and -simplified application code. - -### Installation - -Install OpenBao from Nixpkgs: - -```bash -nix shell nixpkgs#openbao # for a local server -``` - -### Setup - -The setup process can is documented for both local development and production. - -#### Local development - -Start OpenBao in dev mode: - -```bash -bao server -dev -dev-root-token-id="root" -dev-listen-address=127.0.0.1:8201 -``` - -This starts OpenBao on `http://localhost:8201` with a root token. - -Set up environment for bao CLI: - -```bash -export BAO_ADDR=http://localhost:8200 -export BAO_TOKEN=root -``` - -#### Production - -You would typically use a systemd service with a -configuration file. Refer to -[@tangled.org/infra](https://tangled.org/@tangled.org/infra) -for how this can be achieved using Nix. - -Then, initialize the bao server: - -```bash -bao operator init -key-shares=1 -key-threshold=1 -``` - -This will print out an unseal key and a root key. Save them -somewhere (like a password manager). Then unseal the vault -to begin setting it up: - -```bash -bao operator unseal -``` - -All steps below remain the same across both dev and -production setups. - -#### Configure openbao server - -Create the spindle KV mount: - -```bash -bao secrets enable -path=spindle -version=2 kv -``` - -Set up AppRole authentication and policy: - -Create a policy file `spindle-policy.hcl`: - -```hcl -# Full access to spindle KV v2 data -path "spindle/data/*" { - capabilities = ["create", "read", "update", "delete"] -} - -# Access to metadata for listing and management -path "spindle/metadata/*" { - capabilities = ["list", "read", "delete", "update"] -} - -# Allow listing at root level -path "spindle/" { - capabilities = ["list"] -} - -# Required for connection testing and health checks -path "auth/token/lookup-self" { - capabilities = ["read"] -} -``` - -Apply the policy and create an AppRole: - -```bash -bao policy write spindle-policy spindle-policy.hcl -bao auth enable approle -bao write auth/approle/role/spindle \ - token_policies="spindle-policy" \ - token_ttl=1h \ - token_max_ttl=4h \ - bind_secret_id=true \ - secret_id_ttl=0 \ - secret_id_num_uses=0 -``` - -Get the credentials: - -```bash -# Get role ID (static) -ROLE_ID=$(bao read -field=role_id auth/approle/role/spindle/role-id) - -# Generate secret ID -SECRET_ID=$(bao write -f -field=secret_id auth/approle/role/spindle/secret-id) - -echo "Role ID: $ROLE_ID" -echo "Secret ID: $SECRET_ID" -``` - -#### Create proxy configuration - -Create the credential files: - -```bash -# Create directory for OpenBao files -mkdir -p /tmp/openbao - -# Save credentials -echo "$ROLE_ID" > /tmp/openbao/role-id -echo "$SECRET_ID" > /tmp/openbao/secret-id -chmod 600 /tmp/openbao/role-id /tmp/openbao/secret-id -``` - -Create a proxy configuration file `/tmp/openbao/proxy.hcl`: - -```hcl -# OpenBao server connection -vault { - address = "http://localhost:8200" -} - -# Auto-Auth using AppRole -auto_auth { - method "approle" { - mount_path = "auth/approle" - config = { - role_id_file_path = "/tmp/openbao/role-id" - secret_id_file_path = "/tmp/openbao/secret-id" - } - } - - # Optional: write token to file for debugging - sink "file" { - config = { - path = "/tmp/openbao/token" - mode = 0640 - } - } -} - -# Proxy listener for spindle -listener "tcp" { - address = "127.0.0.1:8201" - tls_disable = true -} - -# Enable API proxy with auto-auth token -api_proxy { - use_auto_auth_token = true -} - -# Enable response caching -cache { - use_auto_auth_token = true -} - -# Logging -log_level = "info" -``` - -#### Start the proxy - -Start OpenBao Proxy: - -```bash -bao proxy -config=/tmp/openbao/proxy.hcl -``` - -The proxy will authenticate with OpenBao and start listening on -`127.0.0.1:8201`. - -#### Configure spindle - -Set these environment variables for spindle: - -```bash -export SPINDLE_SERVER_SECRETS_PROVIDER=openbao -export SPINDLE_SERVER_SECRETS_OPENBAO_PROXY_ADDR=http://127.0.0.1:8201 -export SPINDLE_SERVER_SECRETS_OPENBAO_MOUNT=spindle -``` - -On startup, spindle will now connect to the local proxy, -which handles all authentication automatically. - -### Production setup for proxy - -For production, you'll want to run the proxy as a service: - -Place your production configuration in -`/etc/openbao/proxy.hcl` with proper TLS settings for the -vault connection. - -### Verifying setup - -Test the proxy directly: - -```bash -# Check proxy health -curl -H "X-Vault-Request: true" http://127.0.0.1:8201/v1/sys/health - -# Test token lookup through proxy -curl -H "X-Vault-Request: true" http://127.0.0.1:8201/v1/auth/token/lookup-self -``` - -Test OpenBao operations through the server: - -```bash -# List all secrets -bao kv list spindle/ - -# Add a test secret via the spindle API, then check it exists -bao kv list spindle/repos/ - -# Get a specific secret -bao kv get spindle/repos/your_repo_path/SECRET_NAME -``` - -### How it works - -- Spindle connects to OpenBao Proxy on localhost (typically - port 8200 or 8201) -- The proxy authenticates with OpenBao using AppRole - credentials -- All spindle requests go through the proxy, which injects - authentication tokens -- Secrets are stored at - `spindle/repos/{sanitized_repo_path}/{secret_key}` -- Repository paths like `did:plc:alice/myrepo` become - `did_plc_alice_myrepo` -- The proxy handles all token renewal automatically -- Spindle no longer manages tokens or authentication - directly - -### Troubleshooting - -**Connection refused**: Check that the OpenBao Proxy is -running and listening on the configured address. - -**403 errors**: Verify the AppRole credentials are correct -and the policy has the necessary permissions. - -**404 route errors**: The spindle KV mount probably doesn't -exist—run the mount creation step again. - -**Proxy authentication failures**: Check the proxy logs and -verify the role-id and secret-id files are readable and -contain valid credentials. - -**Secret not found after writing**: This can indicate policy -permission issues. Verify the policy includes both -`spindle/data/*` and `spindle/metadata/*` paths with -appropriate capabilities. - -Check proxy logs: - -```bash -# If running as systemd service -journalctl -u openbao-proxy -f - -# If running directly, check the console output -``` - -Test AppRole authentication manually: - -```bash -bao write auth/approle/login \ - role_id="$(cat /tmp/openbao/role-id)" \ - secret_id="$(cat /tmp/openbao/secret-id)" -``` - -# Webhooks - -Webhooks allow you to receive HTTP POST notifications when events occur in your repositories. This enables you to integrate Tangled with external services, trigger CI/CD pipelines, send notifications, or automate workflows. - -## Overview - -Webhooks send HTTP POST requests to URLs you configure whenever specific events happen. Currently, Tangled supports push, repository rename, and pull request events, with more event types coming soon. - -## Configuring webhooks - -To set up a webhook for your repository: - -1. Navigate to your repository -2. Go to **Settings → Hooks** -3. Click **new webhook** -4. Configure your webhook: - - **Payload URL**: The endpoint that will receive the webhook POST requests - - **Secret**: An optional secret key for verifying webhook authenticity (leave blank to send unsigned webhooks) - - **Events**: Select which events trigger the webhook - - **Active**: Toggle whether the webhook is enabled - -## Webhook payload - -### Push - -When a push event occurs, Tangled sends a POST request with a JSON payload of the format: - -```json -{ - "after": "7b320e5cbee2734071e4310c1d9ae401d8f6cab5", - "before": "c04ddf64eddc90e4e2a9846ba3b43e67a0e2865e", - "pusher": { - "did": "did:plc:hwevmowznbiukdf6uk5dwrrq" - }, - "ref": "refs/heads/main", - "repository": { - "clone_url": "https://tangled.org/did:plc:hwevmowznbiukdf6uk5dwrrq/some-repo", - "created_at": "2025-09-15T08:57:23Z", - "description": "an example repository", - "fork": false, - "full_name": "did:plc:hwevmowznbiukdf6uk5dwrrq/some-repo", - "html_url": "https://tangled.org/did:plc:hwevmowznbiukdf6uk5dwrrq/some-repo", - "name": "some-repo", - "open_issues_count": 5, - "owner": { - "did": "did:plc:hwevmowznbiukdf6uk5dwrrq" - }, - "ssh_url": "ssh://git@tangled.org/did:plc:hwevmowznbiukdf6uk5dwrrq/some-repo", - "stars_count": 1, - "updated_at": "2025-09-15T08:57:23Z" - } -} -``` - -### Pull request - -Pull request events are sent as separate event types, so you can subscribe to -exactly the transitions you care about: - -- `pull_request:created` — a pull request was opened -- `pull_request:resubmitted` — a new round (revision) was pushed to a pull request -- `pull_request:merged` — a pull request was merged -- `pull_request:closed` — a pull request was closed -- `pull_request:reopened` — a closed pull request was reopened - -All pull request events share the same payload format: - -```json -{ - "action": "created", - "pull_request": { - "number": 4, - "title": "add dark mode", - "body": "implements dark mode as discussed in #2", - "state": "open", - "target_branch": "main", - "source": { - "branch": "dark-mode", - "sha": "7b320e5cbee2734071e4310c1d9ae401d8f6cab5" - }, - "round_number": 0, - "owner": { - "did": "did:plc:hwevmowznbiukdf6uk5dwrrq" - }, - "html_url": "https://tangled.org/did:plc:hwevmowznbiukdf6uk5dwrrq/some-repo/pulls/4", - "patch_url": "https://tangled.org/did:plc:hwevmowznbiukdf6uk5dwrrq/some-repo/pulls/4/round/0.patch", - "created_at": "2025-09-15T08:57:23Z" - }, - "repository": { ... }, - "sender": { - "did": "did:plc:hwevmowznbiukdf6uk5dwrrq" - } -} -``` - -Notes: - -- `action` mirrors the event type suffix (`created`, `resubmitted`, `merged`, `closed`, `reopened`). -- `repository` has the same format as in the push payload. -- The patch itself is not embedded in the payload (patches can be large); fetch it from `patch_url` instead. `round_number` identifies the latest round, and `patch_url` always points at that round's patch. -- `source` is only present for branch-based and fork-based pull requests; it is omitted for patch-based pulls. For fork-based pulls, `source.repo` contains the DID of the source repository. -- `sender` is the user who performed the action. - -## HTTP headers - -Each webhook request includes the following headers: - -- `Content-Type: application/json` -- `User-Agent: Tangled-Hook/` — User agent with short SHA of the commit (push events); `Tangled-Hook/pull_request` for pull request events -- `X-Tangled-Event: push` — The full event type (e.g. `push`, `pull_request:merged`) -- `X-Tangled-Hook-ID: ` — The webhook ID -- `X-Tangled-Delivery: ` — Unique delivery ID -- `X-Tangled-Signature-256: sha256=` — HMAC-SHA256 signature (if secret configured) - -## Verifying webhook signatures - -If you configured a secret, you should verify the webhook signature to ensure requests are authentic. For example, in Go: - -```go -package main - -import ( - "crypto/hmac" - "crypto/sha256" - "encoding/hex" - "io" - "net/http" - "strings" -) - -func verifySignature(payload []byte, signatureHeader, secret string) bool { - // Remove 'sha256=' prefix from signature header - signature := strings.TrimPrefix(signatureHeader, "sha256=") - - // Compute expected signature - mac := hmac.New(sha256.New, []byte(secret)) - mac.Write(payload) - expected := hex.EncodeToString(mac.Sum(nil)) - - // Use constant-time comparison to prevent timing attacks - return hmac.Equal([]byte(signature), []byte(expected)) -} - -func webhookHandler(w http.ResponseWriter, r *http.Request) { - // Read the request body - payload, err := io.ReadAll(r.Body) - if err != nil { - http.Error(w, "Bad request", http.StatusBadRequest) - return - } - - // Get signature from header - signatureHeader := r.Header.Get("X-Tangled-Signature-256") - - // Verify signature - if signatureHeader != "" && verifySignature(payload, signatureHeader, yourSecret) { - // Webhook is authentic, process it - processWebhook(payload) - w.WriteHeader(http.StatusOK) - } else { - http.Error(w, "Invalid signature", http.StatusUnauthorized) - } -} -``` - -## Delivery retries - -Webhooks are automatically retried on failure: - -- **3 total attempts** (1 initial + 2 retries) -- **Exponential backoff** starting at 1 second, max 10 seconds -- **Retried on**: - - Network errors - - HTTP 5xx server errors -- **Not retried on**: - - HTTP 4xx client errors (bad request, unauthorized, etc.) - -### Timeouts - -Webhook requests timeout after 30 seconds. If your endpoint needs more time: - -1. Respond with 200 OK immediately -2. Process the webhook asynchronously in the background - -## Example integrations - -### Discord notifications - -```javascript -app.post("/webhook", (req, res) => { - const payload = req.body; - - fetch("https://discord.com/api/webhooks/...", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - content: `New push to ${payload.repository.full_name}`, - embeds: [ - { - title: `${payload.pusher.did} pushed to ${payload.ref}`, - url: payload.repository.html_url, - color: 0x00ff00, - }, - ], - }), - }); - - res.status(200).send("OK"); -}); -``` - -# Migrating knots and spindles - -Sometimes, non-backwards compatible changes are made to the -knot/spindle XRPC APIs. If you host a knot or a spindle, you -will need to follow this guide to upgrade. Typically, this -only requires you to deploy the newest version. - -This document is laid out in reverse-chronological order. -Newer migration guides are listed first, and older guides -are further down the page. - -## Upgrading to v1.16.0-alpha - -Starting with v1.16.0-alpha, spindles own CI pipeline data -directly. The appview no longer stores pipeline runs or follows -spindle event streams for pipeline history. Instead, it asks the -configured spindle for pipeline lists, single pipeline details, -workflow logs, retries, and cancellations over XRPC. - -This means that existing pipeline logs / runs won't appear after -you upgrade. Existing pipeline history from the appview cannot be -automatically migrated. If you want to migrate your data, you can -reach out to us and we will send you an SQL file that'll add the data -into your spindle. - -- Upgrade to the latest tag (v1.16.0 or above) -- Head to the [spindle - dashboard](https://tangled.org/settings/spindles) and hit the - "retry" button to verify your spindle - -## Upgrading to v1.15.0-alpha - -With v1.15.0-alpha, a knot itself owns its members and -per-repo collaborators directly. Previously this data was sourced from -PDS records (`sh.tangled.knot.member` and `sh.tangled.repo.collaborator`) -that the appview and the knot both read off the firehose. -The knot is now the source of truth and serves them over XRPC instead: - -- `sh.tangled.knot.addMember`, `sh.tangled.knot.removeMember`, `sh.tangled.knot.listMembers` -- `sh.tangled.repo.addCollaborator`, `sh.tangled.repo.removeCollaborator`, `sh.tangled.repo.listCollaborators` - -Until your knot is upgraded, the appview keeps reading its -members and collaborators from the old firehose-sourced records. -Upgrade to move your knot onto knot-owned access control. - -- Upgrade to the latest tag (v1.15.0 or above) -- Head to the [knot dashboard](https://tangled.org/settings/knots) and - hit the "retry" button to verify your knot - -## Upgrading to v1.14.0-alpha - -Starting with v1.14.0-alpha, the fully knot uses the repoDID as its -canonical handle for repositories. This unlocks repository -renames from the appview UI and changes the wire format for -the following lexicons (`sh.tangled.repo.pull`, `sh.tangled.repo.collaborator`, -`sh.tangled.repo.issue`, `sh.tangled.git.refUpdate`). - -Knots that have not been upgraded may silently drop new push -events, pull requests, issues, and collaborator invites for -repositories they host until upgraded. So upgrade please!!! - -- Upgrade to the latest tag (v1.14.0 or above) -- Head to the [knot dashboard](https://tangled.org/settings/knots) and - hit the "retry" button to verify your knot - -## Upgrading to v1.13.0-alpha - -Starting with v1.13.0-alpha, every repository on a knot is -assigned a DID. This makes repositories stable across -renames and transfers. - -When you upgrade your knot to this version, the server will -automatically mint DIDs for all existing repositories on -startup. This is a one-time process and you may see -additional log output during the first boot as DIDs are -assigned. - -- Upgrade to the latest tag (v1.13.0 or above) -- Head to the [knot dashboard](https://tangled.org/settings/knots) and - hit the "retry" button to verify your knot - -## Upgrading from v1.8.x - -After v1.8.2, the HTTP API for knots and spindles has been -deprecated and replaced with XRPC. Repositories on outdated -knots will not be viewable from the appview. Upgrading is -straightforward however. - -For knots: - -- Upgrade to the latest tag (v1.9.0 or above) -- Head to the [knot dashboard](https://tangled.org/settings/knots) and - hit the "retry" button to verify your knot - -For spindles: - -- Upgrade to the latest tag (v1.9.0 or above) -- Head to the [spindle - dashboard](https://tangled.org/settings/spindles) and hit the - "retry" button to verify your spindle - -## Upgrading from v1.7.x - -After v1.7.0, knot secrets have been deprecated. You no -longer need a secret from the appview to run a knot. All -authorized commands to knots are managed via [Inter-Service -Authentication](https://atproto.com/specs/xrpc#inter-service-authentication-jwt). -Knots will be read-only until upgraded. - -Upgrading is quite easy, in essence: - -- `KNOT_SERVER_SECRET` is no more, you can remove this - environment variable entirely -- `KNOT_SERVER_OWNER` is now required on boot, set this to - your DID. You can find your DID in the - [settings](https://tangled.org/settings) page. -- Restart your knot once you have replaced the environment - variable -- Head to the [knot dashboard](https://tangled.org/settings/knots) and - hit the "retry" button to verify your knot. This simply - writes a `sh.tangled.knot` record to your PDS. - -If you use the nix module, simply bump the flake to the -latest revision, and change your config block like so: - -```diff - services.tangled.knot = { - enable = true; - server = { -- secretFile = /path/to/secret; -+ owner = "did:plc:foo"; - }; - }; -``` - -# Bobbin - -Bobbin is an API appview for Tangled records. It serves XRPC -endpoints for `sh.tangled.*`, with it you can get repos, -issues, pulls, comments, follows, stars, labels, pipelines, -and profiles. It is read-only, there is no auth, since that -should all be handled direct-to-PDS and knot respectively. - -**Bobbin has no permanent storage**. - -It is only a glorified edge index, in the graph theory -sense. Additionally it has a record cache, re-filled on -demand. All other data that Bobbin serves comes live from -PDSes & knots. - -## What Bobbin needs - -The way that Bobbin is able to pull off being -so stateless is by moving state upstream. -Primarily it depends on an instance of -[Hydrant](https://tangled.org/did:plc:6v3ul2ptnqctyxwkz5ti4amn) -, which is the service that gives an event stream -for Bobbin to quickly backfill from on every restart. -Backfilling ought to take less than a couple of minutes -maximum. If the upstream instance of Hydrant fails -while Bobbin is live, its list/count endpoints stop -advancing and report a stale cursor. Single-lookups -will continue working, due to the second dependency: -[Slingshot](https://tangled.org/did:plc:c7mc2fn47ihdihul4vjwsuy3/tree/main/slingshot). -Slingshot fetches individual records & resolves identities. -If the upstream instance of Slingshot fails, single-lookups -will fail with a `502` error. There are some aggregation -endpoints that use Slingshot for hydrating, which will also -fail. - -A soft dependency that ought to exist for Bobbin to operate -correctly is simply the plethora of knots that are out -there, that Bobbin talks to directly for git data and, for -knots at v1.15+, members & collaborators. - -## Building Bobbin - -Bobbin is under [Tangled's core monorepo, under bobbin/](https://tangled.org/did:plc:j5hmlfdrwkvtxm7cjmu7j2is/tree/master/bobbin). -Here's an easy local debug-build: - -```sh -cargo build -p bobbin -``` - -Bobbin loves being in a container. When using -`bobbin/containerfiles/bobbin.Containerfile`, it runs `cargo -build --release --bin bobbin --package bobbin` within a -little Debian runtime, exposing port 8090. - -## Configuration - -The best way to configure Bobbin is via a toml config file. -There's an `example.toml` in [Bobbin's subdir](https://tangled.org/did:plc:j5hmlfdrwkvtxm7cjmu7j2is/blob/master/bobbin/example.toml). -Every value is overridable by a `BOBBIN_*` env var. -The load order is env, then `--config `, then -`/etc/bobbin/config.toml`, then built-in defaults. - -Load and check a config without starting the server: - -```sh -bobbin --config config.toml validate -``` - -Minimal config is the two upstream URLs. The hydrant URL -takes `ws://` or `wss://`. An `http://` or `https://` -URL is rewritten to the matching websocket scheme at -connection-time. - -```toml -[server] -binds = ["127.0.0.1:8090"] - -# Loopback-only & can leave empty to disable debug introspection. -debug_bind = "127.0.0.1:8091" - -[hydrant] -url = "https://hydrant.example.com" - -[slingshot] -url = "https://slingshot.example.com" -``` - -> 🦪 Lewis -> -> At time of writing, we (Tangled) don't host public -> instances of Hydrant or Slingshot. You will have to -> find public instances or spin these up yourself! :P - -Take a gander in the project's example.toml for an -exhaustive list of things to configure. - -You will discover fun things such as a configurable adaptive -loop that watches the cgroup memory limit & throttles heavy -requests under pressure. It only works if it detects a -cgroup limit is present. The config for that is in the -`[backpressure]` block of the config template. - -## Running Bobbin - -Start the server using a config toml: - -```bash -bobbin --config config.toml -``` -Bobbin wakes up in a cold sweat and immediately gets to -work: -1. It binds its listeners, connects to the Hydrant stream - in the background. -2. It serves requests from the first - moment it's alive, even before the Hydrant stream connects - or finishes catching up. Having a cold Hydrant itself - costs only latency and approximate counts. - -## The API - -**Single lookups** take a record's AT-URI. - -- `getRepo` takes the repo URI: - -```sh -curl "$BOBBIN/xrpc/sh.tangled.repo.getRepo?repo=at://did:plc:boltless/sh.tangled.repo/squid" -``` -```json -{ - "uri": "at://did:plc:boltless/sh.tangled.repo/squid", - "cid": "bafyrei...", - "value": { "$type": "sh.tangled.repo", "knot": "knot1.tangled.sh", "description": "...", "createdAt": "..." } -} -``` - -- `getProfile` takes the full profile record URI, so a bare - handle or DID will not resolve: - -```sh -curl "$BOBBIN/xrpc/sh.tangled.actor.getProfile?actor=at://did:plc:boltless/sh.tangled.actor.profile/self" -``` - -- If Slingshot cannot serve the record, the response is `502`: - -```json -{ "error": "UpstreamFailed", "message": "upstream unavailable: ..." } -``` - -**Aggregation** endpoints come in `list*` and `count*` pairs, -each with a `*By` sibling, and require a `subject` query param. - -- `listRepos` and `countRepos` key on the owner DID: - -```sh -curl "$BOBBIN/xrpc/sh.tangled.repo.countRepos?subject=did:plc:boltless" -``` -```json -{ "count": 7, "distinctAuthors": 1 } -``` - -```sh -curl "$BOBBIN/xrpc/sh.tangled.repo.listRepos?subject=did:plc:boltless&limit=3" -``` -```json -{ "items": [ { "uri": "at://did:plc:boltless/sh.tangled.repo/squid", "cid": "bafyrei...", "value": { } } ], "cursor": null } -``` - -- Bobbin validates the subject per collection. Here a repo URI - is passed where a bare DID is required, so the call returns a - `400`: - -```sh -curl "$BOBBIN/xrpc/sh.tangled.graph.listFollows?subject=at://did:plc:boltless/sh.tangled.repo/squid" -``` -```json -{ "error": "InvalidRequest", "message": "invalid request: subject must be a bare did, got at-uri with collection sh.tangled.repo" } -``` - -**Search** is a single endpoint over an in-mem full-text -index: - -```sh -curl "$BOBBIN/xrpc/sh.tangled.search.query?q=tangled&limit=2" -``` -```json -{ "hits": [ { "uri": "at://...", "cid": "...", "nsid": "sh.tangled.repo", "score": 27.1, "value": { } } ], "cursor": null } -``` - -**Git data** such as blob, tree, diff, log, and archive proxies -straight to the repo's knot, streamed back without caching. - -## Coverage and warm-up - -- While the edge index is catching up from Hydrant, - the aggregation count is a lower bound & may still climb. -- One endpoint reports how far along the backfill it is: - -```sh -curl "$BOBBIN/xrpc/sh.tangled.bobbin.getCoverage" -``` - -While warming up: - -```json -{ "ready": false, "eventsProcessed": 45588, "lastCursor": 51658 } -``` - -Once caught up, Bobbin flips to ready: - -```json -{ "ready": true, "eventsProcessed": 106085, "lastCursor": 116527 } -``` - -If starting up Hydrant for the first time, Hydrant itself -will take a decent while (a couple of hours) to backfill -from PDSes. Hydrant stores its backfill on disk. Bobbin -restart reaches `ready` in minutes by replaying event from -an already-populated Hydrant. If your Hydrant is new, expect -Bobbin to backfill in that same couple of hours that Hydrant -takes. - -## Loose ends and not-gonna-impl - -- **No coverage signal for per-knot rosters yet.** - Coverage tracks the hydrant stream only. A v1.15 knot - that is unreachable serves a stale or empty member set - with nothing to flag it. -- **Knot eventstream fan-out isn't pooled.** - Bobbin opens one websocket per v1.15 - knot on top of the hydrant subscription. A network with - thousands of knots wants pooling or a shared subscription. -- **No sequential issue or PR numbers.** bobbin returns rkeys, - not `#42` style ids like the web appview. A client - deriving a display number does it from creation order. But - why bother? rkeys are the IDs. - -# Hacking on Tangled - -We highly recommend [installing -Nix](https://nixos.org/download/) (the package manager) -before working on the codebase. The Nix flake provides a lot -of helpers to get started and most importantly, builds and -dev shells are entirely deterministic. - -To set up your dev environment: - -```bash -nix develop -``` - -Non-Nix users can look at the `devShell` attribute in the -`flake.nix` file to determine necessary dependencies. - -## Running the appview - -The appview requires Redis and OAuth JWKs. Start these -first, before launching the appview itself. - -```bash -# OAuth JWKs should already be set up by the Nix devshell: -echo $TANGLED_OAUTH_CLIENT_SECRET -z42ty4RT1ovnTopY8B8ekz9NuziF2CuMkZ7rbRFpAR9jBqMc - -echo $TANGLED_OAUTH_CLIENT_KID -1761667908 - -# if not, you can set it up yourself: -goat key generate -t P-256 -Key Type: P-256 / secp256r1 / ES256 private key -Secret Key (Multibase Syntax): save this securely (eg, add to password manager) - z42tuPDKRfM2mz2Kv953ARen2jmrPA8S9LX9tRq4RVcUMwwL -Public Key (DID Key Syntax): share or publish this (eg, in DID document) - did:key:zDnaeUBxtG6Xuv3ATJE4GaWeyXM3jyamJsZw3bSPpxx4bNXDR - -# the secret key from above -export TANGLED_OAUTH_CLIENT_SECRET="z42tuP..." - -# Run Redis in a new shell to store OAuth sessions -redis-server -``` - -The Nix flake exposes a few `app` attributes (run `nix -flake show` to see a full list of what the flake provides), -one of the apps runs the appview with the `air` -live-reloader: - -```bash -TANGLED_DEV=true nix run .#watch-appview - -# TANGLED_DB_PATH might be of interest to point to -# different sqlite DBs - -# in a separate shell, you can live-reload tailwind -nix run .#watch-tailwind -``` - -## Running knots and spindles - -An end-to-end knot setup requires setting up a machine with -`sshd`, `AuthorizedKeysCommand`, and a Git user, which is -quite cumbersome. So the Nix flake provides a -`nixosConfiguration` to do so. - -
- macOS users will have to set up a Nix Builder first - -In order to build Tangled's dev VM on macOS, you will -first need to set up a Linux Nix builder. The recommended -way to do so is to run a [`darwin.linux-builder` -VM](https://nixos.org/manual/nixpkgs/unstable/#sec-darwin-builder) -and to register it in `nix.conf` as a builder for Linux -with the same architecture as your Mac (`linux-aarch64` if -you are using Apple Silicon). - -If you're on nix-darwin, you can simply add - -``` -nix.linux-builder.enable = true; -``` - -to your host's `configuration.nix`. - -Alternatively, you can use any other method to set up a -Linux machine with Nix installed that you can `sudo ssh` -into (in other words, root user on your Mac has to be able -to ssh into the Linux machine without entering a password) -and that has the same architecture as your Mac. See -[remote builder -instructions](https://nix.dev/manual/nix/2.28/advanced-topics/distributed-builds.html#requirements) -for how to register such a builder in `nix.conf`. - -> WARNING: If you'd like to use -> [`nixos-lima`](https://github.com/nixos-lima/nixos-lima) or -> [Orbstack](https://orbstack.dev/), note that setting them up so that `sudo -ssh` works can be tricky. It seems to be [possible with -> Orbstack](https://github.com/orgs/orbstack/discussions/1669). - -
- -To begin, grab your DID from http://localhost:3000/settings. -Then, set `TANGLED_VM_KNOT_OWNER` and -`TANGLED_VM_SPINDLE_OWNER` to your DID. You can now start a -lightweight NixOS VM like so: - -```bash -nix run --impure .#vm - -# type `poweroff` at the shell to exit the VM -``` - -This starts a knot on port 6444, a spindle on port 6555 -with `ssh` exposed on port 2222. - -Once the services are running, head to -http://localhost:3000/settings/knots and hit "Verify". It should -verify the ownership of the services instantly if everything -went smoothly. - -You can push repositories to this VM with this ssh config -block on your main machine: - -```bash -Host nixos-shell - Hostname localhost - Port 2222 - User git - IdentityFile ~/.ssh/my_tangled_key -``` - -Set up a remote called `local-dev` on a git repo: - -```bash -git remote add local-dev git@nixos-shell:user/repo -git push local-dev main -``` - -The above VM should already be running a spindle on -`localhost:6555`. Head to http://localhost:3000/settings/spindles and -hit "Verify". You can then configure each repository to use -this spindle and run CI jobs. - -Of interest when debugging spindles: - -``` -# Service logs from journald: -journalctl -xeu spindle - -# CI job logs from disk: -ls /var/log/spindle - -# Debugging spindle database: -sqlite3 /var/lib/spindle/spindle.db - -# litecli has a nicer REPL interface: -litecli /var/lib/spindle/spindle.db -``` - -If for any reason you wish to disable either one of the -services in the VM, modify [nix/vm.nix](/nix/vm.nix) and set -`services.tangled.spindle.enable` (or -`services.tangled.knot.enable`) to `false`. - -# Contribution guide - -## Commit guidelines - -We follow a commit style similar to the Go project. Please keep commits: - -- **atomic**: each commit should represent one logical change -- **descriptive**: the commit message should clearly describe what the - change does and why it's needed - -### Message format - -``` -/: - -Optional longer description can go here, if necessary. Explain what the -change does and why, especially if not obvious. Reference relevant -issues or PRs when applicable. These can be links for now since we don't -auto-link issues/PRs yet. -``` - -Here are some examples: - -``` -appview/state: fix token expiry check in middleware - -The previous check did not account for clock drift, leading to premature -token invalidation. -``` - -``` -knotserver/git/service: improve error checking in upload-pack -``` - -### General notes - -- PRs get merged "as-is" (fast-forward)—like applying a patch-series - using `git am`. At present, there is no squashing—so please author - your commits as they would appear on `master`, following the above - guidelines. -- If there is a lot of nesting, for example "appview: - pages/templates/repo/fragments: ...", these can be truncated down to - just "appview: repo/fragments: ...". If the change affects a lot of - subdirectories, you may abbreviate to just the top-level names, e.g. - "appview: ..." or "knotserver: ...". -- Keep commits lowercased with no trailing period. -- Use the imperative mood in the summary line (e.g., "fix bug" not - "fixed bug" or "fixes bug"). -- Try to keep the summary line under 72 characters, but we aren't too - fussed about this. -- Follow the same formatting for PR titles if filled manually. -- Don't include unrelated changes in the same commit. -- Avoid noisy commit messages like "wip" or "final fix"—rewrite history - before submitting if necessary. - -## Code formatting - -We use a variety of tools to format our code, and multiplex them with -[`treefmt`](https://treefmt.com). All you need to do to format your changes -is run `nix run .#fmt` (or just `treefmt` if you're in the devshell). - -## Proposals for bigger changes - -Small fixes like typos, minor bugs, or trivial refactors can be -submitted directly as PRs. - -For larger changes—especially those introducing new features, significant -refactoring, or altering system behavior—please open a proposal first. This -helps us evaluate the scope, design, and potential impact before implementation. - -Create a new issue titled: - -``` -proposal: : -``` - -In the description, explain: - -- What the change is -- Why it's needed -- How you plan to implement it (roughly) -- Any open questions or tradeoffs - -We'll use the issue thread to discuss and refine the idea before moving -forward. - -## Developer Certificate of Origin (DCO) - -We require all contributors to certify that they have the right to -submit the code they're contributing. To do this, we follow the -[Developer Certificate of Origin -(DCO)](https://developercertificate.org/). - -By signing your commits, you're stating that the contribution is your -own work, or that you have the right to submit it under the project's -license. This helps us keep things clean and legally sound. - -To sign your commit, just add the `-s` flag when committing: - -```sh -git commit -s -m "your commit message" -``` - -This appends a line like: - -``` -Signed-off-by: Your Name -``` - -We won't merge commits if they aren't signed off. If you forget, you can -amend the last commit like this: - -```sh -git commit --amend -s -``` - -If you're submitting a PR with multiple commits, make sure each one is -signed. - -For [jj](https://jj-vcs.github.io/jj/latest/) users, you can run the following command -to make it sign off commits in the tangled repo: - -```shell -# Safety check, should say "No matching config key..." -jj config list templates.commit_trailers -# The command below may need to be adjusted if the command above returned something. -jj config set --repo templates.commit_trailers "format_signed_off_by_trailer(self)" -``` - -Refer to the [jujutsu -documentation](https://jj-vcs.github.io/jj/latest/config/#commit-trailers) -for more information. - -# Troubleshooting guide - -## Login issues - -Owing to the distributed nature of OAuth on AT Protocol, you -may run into issues with logging in. If you run a -self-hosted PDS: - -- You may need to ensure that your PDS is timesynced using - NTP: - - Enable the `ntpd` service - - Run `ntpd -qg` to synchronize your clock -- You may need to increase the default request timeout: - `NODE_OPTIONS="--network-family-autoselection-attempt-timeout=500"` - -## Empty punchcard - -For Tangled to register commits that you make across the -network, you need to setup one of following: - -- The committer email should be a verified email associated - to your account. You can add and verify emails on the - settings page. -- Or, the committer email should be set to your account's - DID: `git config user.email "did:plc:foobar"`. You can find - your account's DID on the settings page - -## Commit is not marked as verified - -Tangled only supports SSH commit signatures. Ensure the SSH public key you use -for signing is uploaded to Tangled. - -To sign commits using an SSH key with git: - -``` -git config --global gpg.format ssh -git config --global user.signingkey ~/.ssh/tangled-key -``` - -To sign commits using an SSH key with jj, add this to your -config: - -``` -[signing] -behavior = "own" -backend = "ssh" -key = "~/.ssh/tangled-key" -``` - -## Self-hosted knot issues - -If you need help troubleshooting a self-hosted knot, check -out the [knot troubleshooting -guide](/knot-self-hosting-guide.html#troubleshooting). diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs new file mode 100644 index 00000000..a7702123 --- /dev/null +++ b/docs/astro.config.mjs @@ -0,0 +1,120 @@ +// @ts-check +import { defineConfig } from "astro/config"; +import starlight from "@astrojs/starlight"; + +export default defineConfig({ + site: "https://docs.tangled.org", + integrations: [ + starlight({ + title: "Tangled Docs", + titleDelimiter: "·", + description: + "Documentation for Tangled — decentralized code collaboration built on the AT Protocol.", + favicon: "/favicon.svg", + head: [ + { + tag: "link", + attrs: { rel: "apple-touch-icon", href: "/apple-touch-icon.png" }, + }, + ], + logo: { + light: "./src/assets/logotype-light.svg", + dark: "./src/assets/logotype-dark.svg", + replacesTitle: true, + }, + social: [ + { + icon: "seti:git", + label: "Source", + href: "https://tangled.org/@tangled.org/core", + }, + ], + editLink: { + baseUrl: "https://tangled.org/@tangled.org/core/blob/master/docs/", + }, + customCss: [ + "./src/styles/tangled.css", + "./src/styles/custom.css", + "./src/styles/sidebar-icons.css", + ], + components: { + SocialIcons: "./src/components/HeaderLinks.astro", + }, + expressiveCode: { + // same palettes as the appview's chroma stylesheet + themes: ["catppuccin-macchiato", "catppuccin-latte"], + styleOverrides: { + borderRadius: "0.25rem", + borderColor: "var(--sl-color-gray-5)", + }, + }, + sidebar: [ + { + label: "Getting started", + items: [ + { slug: "quick-start", attrs: { "data-icon": "rocket" } }, + { slug: "why-atproto", attrs: { "data-icon": "at-sign" } }, + { slug: "sites", attrs: { "data-icon": "globe" } }, + { slug: "troubleshooting", attrs: { "data-icon": "life-buoy" } }, + ], + }, + { + label: "Knots", + items: [ + { + label: "What are knots?", + slug: "knots", + attrs: { "data-icon": "server" }, + }, + { + label: "Self-hosting a knot", + slug: "knots/self-hosting", + attrs: { "data-icon": "hard-drive" }, + }, + ], + }, + { + label: "Spindles", + items: [ + { + label: "What are spindles?", + slug: "spindles", + attrs: { "data-icon": "workflow" }, + }, + { slug: "spindles/workflows", attrs: { "data-icon": "play" } }, + { + slug: "spindles/self-hosting", + attrs: { "data-icon": "hard-drive" }, + }, + { slug: "spindles/secrets", attrs: { "data-icon": "key-round" } }, + { + slug: "spindles/architecture", + attrs: { "data-icon": "network" }, + }, + ], + }, + { + label: "Reference", + items: [ + { slug: "reference/webhooks", attrs: { "data-icon": "webhook" } }, + { slug: "reference/bobbin", attrs: { "data-icon": "cable" } }, + { + slug: "reference/migrations", + attrs: { "data-icon": "arrow-up-circle" }, + }, + ], + }, + { + label: "Contributing", + items: [ + { slug: "contributing/hacking", attrs: { "data-icon": "hammer" } }, + { + slug: "contributing/guide", + attrs: { "data-icon": "git-pull-request" }, + }, + ], + }, + ], + }), + ], +}); diff --git a/docs/highlight.theme b/docs/highlight.theme deleted file mode 100644 index d700e158..00000000 --- a/docs/highlight.theme +++ /dev/null @@ -1,93 +0,0 @@ -{ - "text-color": null, - "background-color": null, - "line-number-color": null, - "line-number-background-color": null, - "text-styles": { - "Annotation": { - "text-color": null, - "background-color": null, - "bold": false, - "italic": true, - "underline": false - }, - "ControlFlow": { - "text-color": null, - "background-color": null, - "bold": true, - "italic": false, - "underline": false - }, - "Error": { - "text-color": null, - "background-color": null, - "bold": true, - "italic": false, - "underline": false - }, - "Alert": { - "text-color": null, - "background-color": null, - "bold": true, - "italic": false, - "underline": false - }, - "Preprocessor": { - "text-color": null, - "background-color": null, - "bold": true, - "italic": false, - "underline": false - }, - "Information": { - "text-color": null, - "background-color": null, - "bold": false, - "italic": true, - "underline": false - }, - "Warning": { - "text-color": null, - "background-color": null, - "bold": false, - "italic": true, - "underline": false - }, - "Documentation": { - "text-color": null, - "background-color": null, - "bold": false, - "italic": true, - "underline": false - }, - "DataType": { - "text-color": "#8f4e8b", - "background-color": null, - "bold": false, - "italic": false, - "underline": false - }, - "Comment": { - "text-color": null, - "background-color": null, - "bold": false, - "italic": true, - "underline": false - }, - "CommentVar": { - "text-color": null, - "background-color": null, - "bold": false, - "italic": true, - "underline": false - }, - "Keyword": { - "text-color": null, - "background-color": null, - "bold": true, - "italic": false, - "underline": false - } - } -} - diff --git a/docs/logo.html b/docs/logo.html deleted file mode 100644 index 4269a4ff..00000000 --- a/docs/logo.html +++ /dev/null @@ -1,6 +0,0 @@ -
- - ${ dolly.svg() } - - tangled -
diff --git a/docs/mode.html b/docs/mode.html deleted file mode 100644 index e67a7b31..00000000 --- a/docs/mode.html +++ /dev/null @@ -1,3 +0,0 @@ - - $if(single-page)$View as multi-page$else$View as single-page$endif$ - diff --git a/docs/package-lock.json b/docs/package-lock.json new file mode 100644 index 00000000..6d7cc2e5 --- /dev/null +++ b/docs/package-lock.json @@ -0,0 +1,7820 @@ +{ + "name": "tangled-docs", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "tangled-docs", + "version": "0.0.1", + "dependencies": { + "@astrojs/starlight": "^0.36.0", + "@fontsource-variable/inter": "^5.2.5", + "@fontsource/ibm-plex-mono": "^5.2.5", + "astro": "^5.14.0", + "lucide-astro": "^0.545.0", + "sharp": "^0.34.0" + }, + "devDependencies": { + "@tailwindcss/typography": "^0.5.20", + "tailwindcss": "^3.4.19" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@astrojs/compiler": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.13.1.tgz", + "integrity": "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==", + "license": "MIT" + }, + "node_modules/@astrojs/internal-helpers": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.6.tgz", + "integrity": "sha512-GOle7smBWKfMSP8osUIGOlB5kaHdQLV3foCsf+5Q9Wsuu+C6Fs3Ez/ttXmhjZ1HkSgsogcM1RXSjjOVieHq16Q==", + "license": "MIT" + }, + "node_modules/@astrojs/markdown-remark": { + "version": "6.3.11", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.11.tgz", + "integrity": "sha512-hcaxX/5aC6lQgHeGh1i+aauvSwIT6cfyFjKWvExYSxUhZZBBdvCliOtu06gbQyhbe0pGJNoNmqNlQZ5zYUuIyQ==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.7.6", + "@astrojs/prism": "3.3.0", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "hast-util-to-text": "^4.0.2", + "import-meta-resolve": "^4.2.0", + "js-yaml": "^4.1.1", + "mdast-util-definitions": "^6.0.0", + "rehype-raw": "^7.0.0", + "rehype-stringify": "^10.0.1", + "remark-gfm": "^4.0.1", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.2", + "remark-smartypants": "^3.0.2", + "shiki": "^3.21.0", + "smol-toml": "^1.6.0", + "unified": "^11.0.5", + "unist-util-remove-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "unist-util-visit-parents": "^6.0.2", + "vfile": "^6.0.3" + } + }, + "node_modules/@astrojs/mdx": { + "version": "4.3.14", + "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-4.3.14.tgz", + "integrity": "sha512-FBrqJQORVm+rkRa2TS5CjU9PBA6hkhrwLVBSS9A77gN2+iehvjq1w6yya/d0YKC7osiVorKkr3Qd9wNbl0ZkGA==", + "license": "MIT", + "dependencies": { + "@astrojs/markdown-remark": "6.3.11", + "@mdx-js/mdx": "^3.1.1", + "acorn": "^8.15.0", + "es-module-lexer": "^1.7.0", + "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.2", + "source-map": "^0.7.6", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.3" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + }, + "peerDependencies": { + "astro": "^5.0.0" + } + }, + "node_modules/@astrojs/prism": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.3.0.tgz", + "integrity": "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==", + "license": "MIT", + "dependencies": { + "prismjs": "^1.30.0" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + } + }, + "node_modules/@astrojs/sitemap": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.7.3.tgz", + "integrity": "sha512-f8euLVsyeAmAkSm/1M2Kb8sL8byQmfgbvBNaHFItCheTj/IpiJYSEWVcqDHZ/yEHxiS7+w87mQkzwZaPHmk5GA==", + "license": "MIT", + "dependencies": { + "sitemap": "^9.0.0", + "stream-replace-string": "^2.0.0", + "zod": "^4.3.6" + } + }, + "node_modules/@astrojs/starlight": { + "version": "0.36.3", + "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.36.3.tgz", + "integrity": "sha512-5cm4QVQHUP6ZE52O43TtUpsTvLKdZa9XEs4l3suzuY7Ymsbz4ojtoL9NhistbMqM+/qk6fm6SmxbOL6hQ/LfNA==", + "license": "MIT", + "dependencies": { + "@astrojs/markdown-remark": "^6.3.1", + "@astrojs/mdx": "^4.2.3", + "@astrojs/sitemap": "^3.3.0", + "@pagefind/default-ui": "^1.3.0", + "@types/hast": "^3.0.4", + "@types/js-yaml": "^4.0.9", + "@types/mdast": "^4.0.4", + "astro-expressive-code": "^0.41.1", + "bcp-47": "^2.1.0", + "hast-util-from-html": "^2.0.1", + "hast-util-select": "^6.0.2", + "hast-util-to-string": "^3.0.0", + "hastscript": "^9.0.0", + "i18next": "^23.11.5", + "js-yaml": "^4.1.0", + "klona": "^2.0.6", + "mdast-util-directive": "^3.0.0", + "mdast-util-to-markdown": "^2.1.0", + "mdast-util-to-string": "^4.0.0", + "pagefind": "^1.3.0", + "rehype": "^13.0.1", + "rehype-format": "^5.0.0", + "remark-directive": "^3.0.0", + "ultrahtml": "^1.6.0", + "unified": "^11.0.5", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.2" + }, + "peerDependencies": { + "astro": "^5.5.0" + } + }, + "node_modules/@astrojs/telemetry": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.0.tgz", + "integrity": "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==", + "license": "MIT", + "dependencies": { + "ci-info": "^4.2.0", + "debug": "^4.4.0", + "dlv": "^1.1.3", + "dset": "^3.1.4", + "is-docker": "^3.0.0", + "is-wsl": "^3.1.0", + "which-pm-runs": "^1.1.0" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@capsizecss/unpack": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.1.tgz", + "integrity": "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.2.0.tgz", + "integrity": "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@expressive-code/core": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.41.7.tgz", + "integrity": "sha512-ck92uZYZ9Wba2zxkiZLsZGi9N54pMSAVdrI9uW3Oo9AtLglD5RmrdTwbYPCT2S/jC36JGB2i+pnQtBm/Ib2+dg==", + "license": "MIT", + "dependencies": { + "@ctrl/tinycolor": "^4.0.4", + "hast-util-select": "^6.0.2", + "hast-util-to-html": "^9.0.1", + "hast-util-to-text": "^4.0.1", + "hastscript": "^9.0.0", + "postcss": "^8.4.38", + "postcss-nested": "^6.0.1", + "unist-util-visit": "^5.0.0", + "unist-util-visit-parents": "^6.0.1" + } + }, + "node_modules/@expressive-code/plugin-frames": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.41.7.tgz", + "integrity": "sha512-diKtxjQw/979cTglRFaMCY/sR6hWF0kSMg8jsKLXaZBSfGS0I/Hoe7Qds3vVEgeoW+GHHQzMcwvgx/MOIXhrTA==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.41.7" + } + }, + "node_modules/@expressive-code/plugin-shiki": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.41.7.tgz", + "integrity": "sha512-DL605bLrUOgqTdZ0Ot5MlTaWzppRkzzqzeGEu7ODnHF39IkEBbFdsC7pbl3LbUQ1DFtnfx6rD54k/cdofbW6KQ==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.41.7", + "shiki": "^3.2.2" + } + }, + "node_modules/@expressive-code/plugin-text-markers": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.41.7.tgz", + "integrity": "sha512-Ewpwuc5t6eFdZmWlFyeuy3e1PTQC0jFvw2Q+2bpcWXbOZhPLsT7+h8lsSIJxb5mS7wZko7cKyQ2RLYDyK6Fpmw==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.41.7" + } + }, + "node_modules/@fontsource-variable/inter": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource-variable/inter/-/inter-5.2.8.tgz", + "integrity": "sha512-kOfP2D+ykbcX/P3IFnokOhVRNoTozo5/JxhAIVYLpea/UBmCQ/YWPBfWIDuBImXX/15KH+eKh4xpEUyS2sQQGQ==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/ibm-plex-mono": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@fontsource/ibm-plex-mono/-/ibm-plex-mono-5.2.7.tgz", + "integrity": "sha512-MKAb8qV+CaiMQn2B0dIi1OV3565NYzp3WN5b4oT6LTkk+F0jR6j0ZN+5BKJiIhffDC3rtBULsYZE65+0018z9w==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mdx-js/mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz", + "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdx": "^2.0.0", + "acorn": "^8.0.0", + "collapse-white-space": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-util-scope": "^1.0.0", + "estree-walker": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "markdown-extensions": "^2.0.0", + "recma-build-jsx": "^1.0.0", + "recma-jsx": "^1.0.0", + "recma-stringify": "^1.0.0", + "rehype-recma": "^1.0.0", + "remark-mdx": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "source-map": "^0.7.0", + "unified": "^11.0.0", + "unist-util-position-from-estree": "^2.0.0", + "unist-util-stringify-position": "^4.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@oslojs/encoding": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", + "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", + "license": "MIT" + }, + "node_modules/@pagefind/darwin-arm64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.5.2.tgz", + "integrity": "sha512-MXpI+7HsAdPkvJ0gk9xj9g541BCqBZOBbdwj9g6lB5LCj6kSV6nqDSjzcAJwvOsfu0fjwvC8hQU+ecfhp+MpiQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@pagefind/darwin-x64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.5.2.tgz", + "integrity": "sha512-IojxFWMEJe0RQ7PQ3KXQsPIImNsbpPYpoZ+QUDrL8fAl/O27IX+LVLs74/UzEZy5uA2LD8Nz1AiwKr72vrkZQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@pagefind/default-ui": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/default-ui/-/default-ui-1.5.2.tgz", + "integrity": "sha512-pm1LMnQg8N2B3n2TnjKlhaFihpz6zTiA4HiGQ6/slKO/+8K9CAU5kcjdSSPgpuk1PMuuN4hxLipUIifnrkl3Sg==", + "license": "MIT" + }, + "node_modules/@pagefind/freebsd-x64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.5.2.tgz", + "integrity": "sha512-7EVzo9+0w+2cbe671BtMj10UlNo83I+HrLVLfRxO731svHRJKUfJ/mo05gU14pe9PCfpKNQT8FS3Xc/oDN6pOA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@pagefind/linux-arm64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.5.2.tgz", + "integrity": "sha512-Ovt9+K35sqzn8H3ZMXGwls4TD/wMJuvRtShHIsmUQREmaxjrDEX7gHckRCrwYJ4XE1H1p6HkLz3wukrAnsfXQw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@pagefind/linux-x64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.5.2.tgz", + "integrity": "sha512-V+tFqHKXhQKq/WqPBD67AFy7scn1/aZID00ws4fSDd+1daSi5UHR9VVlRrOUYKxn3VuFQYRD7lYXdZK1WED1YA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@pagefind/windows-arm64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/windows-arm64/-/windows-arm64-1.5.2.tgz", + "integrity": "sha512-hN9Nh90fNW61nNRCW9ZyQrAj/mD0eRvmJ8NlTUzkbuW8kIzGJUi3cxjFkEcMZ5h/8FsKWD/VcouZl4yo1F7B6g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@pagefind/windows-x64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.5.2.tgz", + "integrity": "sha512-Fa2Iyw7kaDRzGMfNYNUXNW2zbL5FQVDgSOcbDHdzBrDEdpqOqg8TcZ68F22ol6NJ9IGzvUdmeyZypLW5dyhqsg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/pluginutils": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", + "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", + "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", + "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", + "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", + "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", + "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", + "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", + "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", + "cpu": [ + "arm" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", + "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", + "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", + "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", + "cpu": [ + "loong64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", + "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", + "cpu": [ + "loong64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", + "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", + "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", + "cpu": [ + "ppc64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", + "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", + "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", + "cpu": [ + "riscv64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", + "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", + "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", + "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", + "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", + "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", + "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", + "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", + "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", + "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/core": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.23.0.tgz", + "integrity": "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.5" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.23.0.tgz", + "integrity": "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.4" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz", + "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz", + "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz", + "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@shikijs/types": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz", + "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "license": "MIT" + }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.20.tgz", + "integrity": "sha512-hwbzQuNUfcPvbegQFatVPl/MY/tcM9KLl963hQ5laJKPh81TEZ1+dNG9PirGvcaDBkp+BCshExAyKVPW91dozw==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >=4.0.0 || insiders" + } + }, + "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdx": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.14.tgz", + "integrity": "sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg==", + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/nlcst": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", + "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/sax": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", + "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==", + "license": "ISC" + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-align/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-iterate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", + "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "license": "MIT", + "bin": { + "astring": "bin/astring" + } + }, + "node_modules/astro": { + "version": "5.18.2", + "resolved": "https://registry.npmjs.org/astro/-/astro-5.18.2.tgz", + "integrity": "sha512-TnFwLnAXty5MXKPDGuKXqK4AMBXG+FH6RUdK7Oyc3gyfNoFIthT+4eRbzOK43bdRlLaZuxgciDSjgtggZ3OtGQ==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler": "^2.13.0", + "@astrojs/internal-helpers": "0.7.6", + "@astrojs/markdown-remark": "6.3.11", + "@astrojs/telemetry": "3.3.0", + "@capsizecss/unpack": "^4.0.0", + "@oslojs/encoding": "^1.1.0", + "@rollup/pluginutils": "^5.3.0", + "acorn": "^8.15.0", + "aria-query": "^5.3.2", + "axobject-query": "^4.1.0", + "boxen": "8.0.1", + "ci-info": "^4.3.1", + "clsx": "^2.1.1", + "common-ancestor-path": "^1.0.1", + "cookie": "^1.1.1", + "cssesc": "^3.0.0", + "debug": "^4.4.3", + "deterministic-object-hash": "^2.0.2", + "devalue": "^5.6.2", + "diff": "^8.0.3", + "dlv": "^1.1.3", + "dset": "^3.1.4", + "es-module-lexer": "^1.7.0", + "esbuild": "^0.27.3", + "estree-walker": "^3.0.3", + "flattie": "^1.1.1", + "fontace": "~0.4.0", + "github-slugger": "^2.0.0", + "html-escaper": "3.0.3", + "http-cache-semantics": "^4.2.0", + "import-meta-resolve": "^4.2.0", + "js-yaml": "^4.1.1", + "magic-string": "^0.30.21", + "magicast": "^0.5.1", + "mrmime": "^2.0.1", + "neotraverse": "^0.6.18", + "p-limit": "^6.2.0", + "p-queue": "^8.1.1", + "package-manager-detector": "^1.6.0", + "piccolore": "^0.1.3", + "picomatch": "^4.0.3", + "prompts": "^2.4.2", + "rehype": "^13.0.2", + "semver": "^7.7.3", + "shiki": "^3.21.0", + "smol-toml": "^1.6.0", + "svgo": "^4.0.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tsconfck": "^3.1.6", + "ultrahtml": "^1.6.0", + "unifont": "~0.7.3", + "unist-util-visit": "^5.0.0", + "unstorage": "^1.17.4", + "vfile": "^6.0.3", + "vite": "^6.4.1", + "vitefu": "^1.1.1", + "xxhash-wasm": "^1.1.0", + "yargs-parser": "^21.1.1", + "yocto-spinner": "^0.2.3", + "zod": "^3.25.76", + "zod-to-json-schema": "^3.25.1", + "zod-to-ts": "^1.2.0" + }, + "bin": { + "astro": "astro.js" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/astrodotbuild" + }, + "optionalDependencies": { + "sharp": "^0.34.0" + } + }, + "node_modules/astro-expressive-code": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.41.7.tgz", + "integrity": "sha512-hUpogGc6DdAd+I7pPXsctyYPRBJDK7Q7d06s4cyP0Vz3OcbziP3FNzN0jZci1BpCvLn9675DvS7B9ctKKX64JQ==", + "license": "MIT", + "dependencies": { + "rehype-expressive-code": "^0.41.7" + }, + "peerDependencies": { + "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta" + } + }, + "node_modules/astro/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/astro/node_modules/zod-to-ts": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/zod-to-ts/-/zod-to-ts-1.2.0.tgz", + "integrity": "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==", + "peerDependencies": { + "typescript": "^4.9.4 || ^5.0.2", + "zod": "^3" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/base-64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", + "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", + "license": "MIT" + }, + "node_modules/bcp-47": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-2.1.1.tgz", + "integrity": "sha512-KLw+H/gd2p4zly1X7Yh/qziuyae5/w/QFnvTng9eZL5fvszL7Whl3MBoWF8yxL7ksUjBfOD+OxkytiqbBpG+Fw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/bcp-47-match": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz", + "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/boxen": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz", + "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^8.0.0", + "chalk": "^5.3.0", + "cli-boxes": "^3.0.0", + "string-width": "^7.2.0", + "type-fest": "^4.21.0", + "widest-line": "^5.0.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/camelcase": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", + "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/collapse-white-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/common-ancestor-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", + "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", + "license": "ISC" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cookie-es": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz", + "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", + "license": "MIT" + }, + "node_modules/crossws": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", + "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", + "license": "MIT", + "dependencies": { + "uncrypto": "^0.1.3" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-selector-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.3.0.tgz", + "integrity": "sha512-Y2asgMGFqJKF4fq4xHDSlFYIkeVfRsm69lQC1q9kbEsH5XtnINTMrweLkjYMeaUgiXBy/uvKeO/a1JHTNnmB2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "license": "CC0-1.0" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/deterministic-object-hash": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/deterministic-object-hash/-/deterministic-object-hash-2.0.2.tgz", + "integrity": "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==", + "license": "MIT", + "dependencies": { + "base-64": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/devalue": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.1.tgz", + "integrity": "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==", + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/direction": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz", + "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==", + "license": "MIT", + "bin": { + "direction": "cli.js" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dset": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "license": "MIT" + }, + "node_modules/esast-util-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", + "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esast-util-from-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", + "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "acorn": "^8.0.0", + "esast-util-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-util-attach-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", + "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", + "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-walker": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-scope": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", + "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", + "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-visit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", + "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/expressive-code": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.41.7.tgz", + "integrity": "sha512-2wZjC8OQ3TaVEMcBtYY4Va3lo6J+Ai9jf3d4dbhURMJcU4Pbqe6EcHe424MIZI0VHUA1bR6xdpoHYi3yxokWqA==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.41.7", + "@expressive-code/plugin-frames": "^0.41.7", + "@expressive-code/plugin-shiki": "^0.41.7", + "@expressive-code/plugin-text-markers": "^0.41.7" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flattie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", + "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/fontace": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.1.tgz", + "integrity": "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.2" + } + }, + "node_modules/fontkitten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.3.tgz", + "integrity": "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==", + "license": "MIT", + "dependencies": { + "tiny-inflate": "^1.0.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "license": "ISC" + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/h3": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz", + "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==", + "license": "MIT", + "dependencies": { + "cookie-es": "^1.2.3", + "crossws": "^0.3.5", + "defu": "^6.1.6", + "destr": "^2.0.5", + "iron-webcrypto": "^1.2.1", + "node-mock-http": "^1.0.4", + "radix3": "^1.1.2", + "ufo": "^1.6.3", + "uncrypto": "^0.1.3" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-util-embedded": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz", + "integrity": "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-is-element": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-format": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hast-util-format/-/hast-util-format-1.1.0.tgz", + "integrity": "sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-minify-whitespace": "^1.0.0", + "hast-util-phrasing": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "html-whitespace-sensitive-tag-names": "^3.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-has-property": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz", + "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-body-ok-link": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-is-body-ok-link/-/hast-util-is-body-ok-link-3.0.1.tgz", + "integrity": "sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-minify-whitespace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hast-util-minify-whitespace/-/hast-util-minify-whitespace-1.0.1.tgz", + "integrity": "sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-phrasing": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-phrasing/-/hast-util-phrasing-3.0.1.tgz", + "integrity": "sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-has-property": "^3.0.0", + "hast-util-is-body-ok-link": "^3.0.0", + "hast-util-is-element": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-select": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.4.tgz", + "integrity": "sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "bcp-47-match": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "css-selector-parser": "^3.0.0", + "devlop": "^1.0.0", + "direction": "^2.0.0", + "hast-util-has-property": "^3.0.0", + "hast-util-to-string": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "nth-check": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-estree": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", + "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-attach-comments": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", + "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-string": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz", + "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/html-whitespace-sensitive-tag-names": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-whitespace-sensitive-tag-names/-/html-whitespace-sensitive-tag-names-3.0.1.tgz", + "integrity": "sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause" + }, + "node_modules/i18next": { + "version": "23.16.8", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.8.tgz", + "integrity": "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "devOptional": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-yaml": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/lucide-astro": { + "version": "0.545.0", + "resolved": "https://registry.npmjs.org/lucide-astro/-/lucide-astro-0.545.0.tgz", + "integrity": "sha512-KBSQTlW4e+tT6DvWEWcqxWa2WubWdqhLG7/WQgnAawQ29ZAEUfTqo2At9IW9xlrG1k8qrVwzeGQfeumiMsgI6Q==", + "deprecated": "Deprecated: Use `@lucide/astro`", + "license": "MIT", + "peerDependencies": { + "astro": ">=2.7.1" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.3.tgz", + "integrity": "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, + "node_modules/markdown-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-definitions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz", + "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-directive": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.1.0.tgz", + "integrity": "sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", + "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "license": "CC0-1.0" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", + "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", + "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-jsx": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", + "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-md": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", + "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", + "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", + "license": "MIT", + "dependencies": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^3.0.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-extension-mdxjs-esm": "^3.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", + "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", + "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-events-to-acorn": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", + "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neotraverse": { + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz", + "integrity": "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/nlcst-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", + "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, + "node_modules/node-mock-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.4.tgz", + "integrity": "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/ofetch": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", + "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", + "license": "MIT", + "dependencies": { + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "license": "MIT" + }, + "node_modules/oniguruma-parser": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", + "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==", + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz", + "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==", + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.2", + "regex": "^6.1.0", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/p-limit": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.2.0.tgz", + "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.1.tgz", + "integrity": "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1", + "p-timeout": "^6.1.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz", + "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-manager-detector": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.7.0.tgz", + "integrity": "sha512-xg1eHpwYL/D/HEdWw2goFZP6vV0FH7W+PZ5rFkGjdIDLtxq7EkzBUeT3m+lndYCt8wKbmofUu1MUdMCXkCk9ZQ==", + "license": "MIT" + }, + "node_modules/pagefind": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.5.2.tgz", + "integrity": "sha512-XTUaK0hXMCu2jszWE584JGQT7y284TmMV9l/HX3rnG5uo3rHI/uHU56XTyyyPFjeWEBxECbAi0CaFDJOONtG0Q==", + "license": "MIT", + "bin": { + "pagefind": "lib/runner/bin.cjs" + }, + "optionalDependencies": { + "@pagefind/darwin-arm64": "1.5.2", + "@pagefind/darwin-x64": "1.5.2", + "@pagefind/freebsd-x64": "1.5.2", + "@pagefind/linux-arm64": "1.5.2", + "@pagefind/linux-x64": "1.5.2", + "@pagefind/windows-arm64": "1.5.2", + "@pagefind/windows-x64": "1.5.2" + } + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/parse-latin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", + "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "@types/unist": "^3.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-modify-children": "^4.0.0", + "unist-util-visit-children": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/piccolore": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", + "integrity": "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==", + "license": "ISC" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.19", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz", + "integrity": "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz", + "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/property-information": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/radix3": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", + "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", + "license": "MIT" + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/recma-build-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", + "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-build-jsx": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-jsx": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz", + "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==", + "license": "MIT", + "dependencies": { + "acorn-jsx": "^5.0.0", + "estree-util-to-js": "^2.0.0", + "recma-parse": "^1.0.0", + "recma-stringify": "^1.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/recma-parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", + "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "esast-util-from-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", + "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-to-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "license": "MIT" + }, + "node_modules/rehype": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.2.tgz", + "integrity": "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "rehype-parse": "^9.0.0", + "rehype-stringify": "^10.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-expressive-code": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.41.7.tgz", + "integrity": "sha512-25f8ZMSF1d9CMscX7Cft0TSQIqdwjce2gDOvQ+d/w0FovsMwrSt3ODP4P3Z7wO1jsIJ4eYyaDRnIR/27bd/EMQ==", + "license": "MIT", + "dependencies": { + "expressive-code": "^0.41.7" + } + }, + "node_modules/rehype-format": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/rehype-format/-/rehype-format-5.0.1.tgz", + "integrity": "sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-format": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-parse": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", + "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-html": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-recma": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", + "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "hast-util-to-estree": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", + "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-directive": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.1.tgz", + "integrity": "sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-directive": "^3.0.0", + "micromark-extension-directive": "^3.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz", + "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==", + "license": "MIT", + "dependencies": { + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-smartypants": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.2.tgz", + "integrity": "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==", + "license": "MIT", + "dependencies": { + "retext": "^9.0.0", + "retext-smartypants": "^6.0.0", + "unified": "^11.0.4", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/retext": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", + "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "retext-latin": "^4.0.0", + "retext-stringify": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-latin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", + "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "parse-latin": "^7.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-smartypants": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", + "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-stringify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", + "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", + "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.62.2", + "@rollup/rollup-android-arm64": "4.62.2", + "@rollup/rollup-darwin-arm64": "4.62.2", + "@rollup/rollup-darwin-x64": "4.62.2", + "@rollup/rollup-freebsd-arm64": "4.62.2", + "@rollup/rollup-freebsd-x64": "4.62.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", + "@rollup/rollup-linux-arm-musleabihf": "4.62.2", + "@rollup/rollup-linux-arm64-gnu": "4.62.2", + "@rollup/rollup-linux-arm64-musl": "4.62.2", + "@rollup/rollup-linux-loong64-gnu": "4.62.2", + "@rollup/rollup-linux-loong64-musl": "4.62.2", + "@rollup/rollup-linux-ppc64-gnu": "4.62.2", + "@rollup/rollup-linux-ppc64-musl": "4.62.2", + "@rollup/rollup-linux-riscv64-gnu": "4.62.2", + "@rollup/rollup-linux-riscv64-musl": "4.62.2", + "@rollup/rollup-linux-s390x-gnu": "4.62.2", + "@rollup/rollup-linux-x64-gnu": "4.62.2", + "@rollup/rollup-linux-x64-musl": "4.62.2", + "@rollup/rollup-openbsd-x64": "4.62.2", + "@rollup/rollup-openharmony-arm64": "4.62.2", + "@rollup/rollup-win32-arm64-msvc": "4.62.2", + "@rollup/rollup-win32-ia32-msvc": "4.62.2", + "@rollup/rollup-win32-x64-gnu": "4.62.2", + "@rollup/rollup-win32-x64-msvc": "4.62.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/shiki": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.23.0.tgz", + "integrity": "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "3.23.0", + "@shikijs/engine-javascript": "3.23.0", + "@shikijs/engine-oniguruma": "3.23.0", + "@shikijs/langs": "3.23.0", + "@shikijs/themes": "3.23.0", + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/sitemap": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-9.0.1.tgz", + "integrity": "sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==", + "license": "MIT", + "dependencies": { + "@types/node": "^24.9.2", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.4.1" + }, + "bin": { + "sitemap": "dist/esm/cli.js" + }, + "engines": { + "node": ">=20.19.5", + "npm": ">=10.8.2" + } + }, + "node_modules/smol-toml": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.0.tgz", + "integrity": "sha512-aqVvWoyO21L23mb+drl4RmMXbf6N7FdHjAhTRA9ZBL7apWBgfWC16KjrASI+1p9GAroljyMHj6fK67i0UiTNvQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/stream-replace-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz", + "integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==", + "license": "MIT" + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svgo": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.2.tgz", + "integrity": "sha512-ekx94z1rRc5LDi6oSUaeRnYhd0UOJxdtQCL2rF8xpWxD3TPAsISWOrxezqGovqS38GRZOdpDfvQe3ts6F7nsng==", + "license": "MIT", + "dependencies": { + "commander": "^11.1.0", + "css-select": "^5.1.0", + "css-tree": "^3.0.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "^1.5.0" + }, + "bin": { + "svgo": "bin/svgo.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/tailwindcss/node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tailwindcss/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tailwindcss/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tsconfck": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", + "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", + "deprecated": "unmaintained", + "license": "MIT", + "bin": { + "tsconfck": "bin/tsconfck.js" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "license": "MIT" + }, + "node_modules/ultrahtml": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.7.0.tgz", + "integrity": "sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==", + "license": "MIT" + }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "license": "MIT" + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unifont": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.4.tgz", + "integrity": "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==", + "license": "MIT", + "dependencies": { + "css-tree": "^3.1.0", + "ofetch": "^1.5.1", + "ohash": "^2.0.11" + } + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-modify-children": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", + "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "array-iterate": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", + "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-children": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", + "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unstorage": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", + "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^5.0.0", + "destr": "^2.0.5", + "h3": "^1.15.10", + "lru-cache": "^11.2.7", + "node-fetch-native": "^1.6.7", + "ofetch": "^1.5.1", + "ufo": "^1.6.3" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.8.0", + "@azure/cosmos": "^4.2.0", + "@azure/data-tables": "^13.3.0", + "@azure/identity": "^4.6.0", + "@azure/keyvault-secrets": "^4.9.0", + "@azure/storage-blob": "^12.26.0", + "@capacitor/preferences": "^6 || ^7 || ^8", + "@deno/kv": ">=0.9.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.3", + "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", + "@vercel/kv": "^1 || ^2 || ^3", + "aws4fetch": "^1.0.20", + "db0": ">=0.2.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.2", + "uploadthing": "^7.4.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/functions": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/which-pm-runs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", + "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/widest-line": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", + "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", + "license": "MIT", + "dependencies": { + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/xxhash-wasm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", + "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", + "license": "MIT" + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yocto-spinner": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/yocto-spinner/-/yocto-spinner-0.2.3.tgz", + "integrity": "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==", + "license": "MIT", + "dependencies": { + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": ">=18.19" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 00000000..76e3c890 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,23 @@ +{ + "name": "tangled-docs", + "type": "module", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "astro dev", + "build": "astro build", + "preview": "astro preview" + }, + "dependencies": { + "@astrojs/starlight": "^0.36.0", + "@fontsource-variable/inter": "^5.2.5", + "@fontsource/ibm-plex-mono": "^5.2.5", + "astro": "^5.14.0", + "lucide-astro": "^0.545.0", + "sharp": "^0.34.0" + }, + "devDependencies": { + "@tailwindcss/typography": "^0.5.20", + "tailwindcss": "^3.4.19" + } +} diff --git a/docs/postcss.config.cjs b/docs/postcss.config.cjs new file mode 100644 index 00000000..361ec6e0 --- /dev/null +++ b/docs/postcss.config.cjs @@ -0,0 +1,5 @@ +module.exports = { + plugins: { + tailwindcss: { config: "./tailwind.config.cjs" }, + }, +}; diff --git a/docs/public/_redirects b/docs/public/_redirects new file mode 100644 index 00000000..c1975349 --- /dev/null +++ b/docs/public/_redirects @@ -0,0 +1,12 @@ +# redirects from the old pandoc-generated pages +/quick-start-guide.html /quick-start/ 301 +/hosting-websites-on-tangled.html /sites/ 301 +/knot-self-hosting-guide.html /knots/self-hosting/ 301 +/spindles.html /spindles/workflows/ 301 +/webhooks.html /reference/webhooks/ 301 +/migrating-knots-and-spindles.html /reference/migrations/ 301 +/bobbin.html /reference/bobbin/ 301 +/hacking-on-tangled.html /contributing/hacking/ 301 +/contribution-guide.html /contributing/guide/ 301 +/troubleshooting-guide.html /troubleshooting/ 301 +/single-page.html / 301 diff --git a/docs/public/apple-touch-icon.png b/docs/public/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..2fad9b55a19590c6e1e97f66aa1e47e050d03782 GIT binary patch literal 4666 zcmeAS@N?(olHy`uVBq!ia0y~yVAuk}9Bd2>47O+4j2IY%7(HDaLn`LHnajN;&b72* z|D9@P&c+k3C$Xsr1UR|IJycZE>Ij&+tl{a#X+Z+Jo&-+cY7@h{IjfYjRV!j?Bh%VN zO$&Nmb8~mUi>L&v56d?;HHO`c?^%8|UOeDYnI}-e9l^ARYY&GFvkmVVre}<= zr@o%_!f8|EGmZzZ4;(%aWq4%MCW#%a7Sa-{V^6yL{mD?UR$?AE^E(yh1Ah;sOS%1U zS!ANzu(?54zdiDYHhPPX!)dO|?SnA{(8MjY1 ze{y`98LyD+P1YHLjR$9)df)JOi}cyH#9LeyYOC+RJ5b$d`(%Fkl(*~FHaz@vMk--v zkI#CB^NgPx)l*JqA86ED&J_G)Z>UoC?2`vXlb&=Q@R@M_X^G*5O^dHYA7DNZ-te8- zcG|C$;-~Lj3~o)EYTZy?@oKY+u=QyTBXx)4{}^|$SzK{gZ+IYI*}yIQlZb(xLw%X9 zJWC$yHOS z*3c{4>c@9Renxi9R}(%Qe=TI45_)3#EvwbLpDao_vo+27!IKO6SEUx-*S$XbQpnd7sV~ znP179e5b#VyT)|;#8aO4Em8}&GuW}l$i1lTtdP)qDBtwp_5yW}f~lL<>wmJU=-@_)rDs;cs(TUUkmPdG(34 z(CcDZkEhV~GZPNHySQof9F9!$kV#o5OMZ5_A60K$Drn+2DeIIJ=Z1B1Y>Km`>vfyh zJcIZv&9DMXS)S^8%a#rWE-I*rSEyNhX zAfx=SYpsE;=9`I=_obZa6nLZdk!P>$%>U6W^B1YyRX=@n`7OeHs({udBKfHI7)#^go~TRX&>c z{&JOMKNlbQg1s_da-BX;NwzRpt9U<7NylD9{Rb!OzcR~$^66`;e>F}$u>Oa={HKy%9t&znZLSHtmAR+I zs5tOh!;zoaYQLAvWBFtLA?Wh`J*0u&kidG7^MA73a-xO ztXZEjle=;M3F*`p!2^6ovL-ictJceSWNe@9AF2C2BDyGMrrS{{|B#v8mmg{7eoR#=hGFPK`>n>x~AMBD_+g?m(nDm<|`H8Waf1bL1 z-~-vmN1St-eE8W^)W0b|65o6*vDW8C%&U*@4!q|*skzB{%f$EYAG|;7ioe>r-Adi{ z!auvG@1Ak(7itXJm!h!r?cEKs8P@05Pg*0pYui=j&gx@X+jpoNCEJPq@XvLfA~{8N z=B`WX;oHn4z9`)01-mjZpe@M=CReHpEGj*l5 zy8_$Z?rmyv7XC@?D)-cWPqbgWw#EGz!{h3d+pGL)gnvxiCNyzVKs4idpEno15?^;( zN67@L-&6Y{SCR4U)x_Yjm@{ZTzq1G?ULR7y<@WP)ksDeeZAJsZ+Dc6U+&tZ zsGcNSlWuLvuljGwdAAGk`%?HG{anA+LvrS-Tt(3*n>c=*ZQtW?bnhFnX`LZAkA69E zHuLSjV%_fQCo+3h{gM5!Ynza0O2D$r{7mgfGw#g`khHTcu>KI7tEzS8ePjK_utSD7 zHO^iZtkKFz-O3{CIVE|?(bPLyC%kP#PfA}uBGOqJ7FwmU`kmqdzpkn^EPA0e`?JGR z{(o-fsEaiFShGyaGPF$C_8k3FHknXA^6$u&pi%EfP0Hu+gv%>@>}`E{Y^#Z0#a8Q zv{#){IvE&vOmg0wy0e?^Occ{&6R%zsEtl?g;dts(XJ@ve`9aFJK0IZZSMprSFVk<6 z&iBV+ehKR%r@ifpR&n#3T3Pc#-mm|`^{bv~>>T^d`jOPZ^g#{uK-C{%2uwNyE_j)y%%2-08}FI!iVa&T zwkIj_+q$;%w%W~|W}lBRuF;CQSz&ka)sweHA17W_OVKf#eM4oNTly=xC>61-{MXoJ zJWhR@rP6WNL%X>y_>bh4n<~%NyBBTNG~ICN(ap2E%LHfLE$ZLYXh zvaXj!z$CTx=;nPhm#=MLe7^lw?yHmwGY#i26N$?9d+`0{8p#|b-`wPnYx4b_q~orH zDYU2I8W;>|);wZ(^ya!GD- zYOmcI`Qm2JuILCG>0pCTQ>V4ND_X9;d~Ic?{J3*XEH%t>M$PUPW>>b}`z>?!-!^{!?0d***%|u{*QCFge9@-n z%hHv1n0hYdH7NE?i73$1uGrps{8rfkU)fW;PAurT+Zb>1H}>1s88HcQJNM>PKD+wy z_Oz#FkCM%k_BAQ4XVm);F#S@FTAy;ZOqkhht2vhvx4aW?j-C{JZmQVV8SEld=#)n}-;FDHaNX9c=-Q8W?T!zQt$H%^dYoeUq!~81PJNmurdW_3W#wX8oVcLw zM|u0(2UG2CvRpU2;}CE4OPvwhI3_Tn0o9&hur z&dQyCCLd32`+afaC%XscV&~US&)sf4@fMdzf>*0>-?qmEr@aMs?l5}MZ>6O!lIs7$shFu?(KaEu z7tD4G*SZ*6yM1`MY3dE;l_r<;)#u#Gede84b~8fH?ZpJcIk6x8+RxeS+j;KOw~DJ4 zx1N^ji?i+B-fU!I;TX-xe2%H_a@Zl`+o$gB5{{ABa%!(W`!ptm_cpyjb;eScl7B!w-qMtWuRKO|7nhJL7kMjGDk}RPJ)YOfl6}B~jSy zLkJ6RkXzF1#dXF_W)}t2yT8mx63Q05+<3II@6p7NiALYG%pVC#Mc9tf`hRnpwWvWpeT! zJ>@SG?nr4YejOn>b<1n%ZXS;l?4|R%7Z}EzX8OqT`RGd164p{h>*}21ULxFwcPnLZ%-`gvBwxGef%-;yi`xd9GPj@peOhX9w?qBGray}qgAHq3zJFZO z?Gz*R<*T5YjeiKc`tALCyuVUU8cz2Rd3arqtMKjb>V}wEzA8BpItMitM~d(ndgtk= z3z?lMNIX+fG;5~LIjhbV&2wyy@*4%>MAsXuZ?6#E`rP+{Pxw3GeRrhPy@h_bDD|&Q zy5jdC^@4Z9`D!ieK>c-B!w$5~R@%WjQ6;W$Uc=?@zFnIef6lu%HV-$LkZ*EZ@OYh6*eODIU3wK=*uCvLf=+2ck{VOF$q}~W{m5Y3+7knYnJV)}z zy`~SE38gH(uf4(;=bu~oS+PiJ+1l01?ueb(7h>V_DKFc+D6;aV(xdY~1z)UIuq-SM>PN2P;NV(I~xBK7FwnB&Pn^( z^T^(1e~iFYGpF!PG8SqMyF=#%8fE!^&bZTm^4hH#2TE_t{D@;Y77_N0xtcX^ca%Yp zTVp{>r#$g<`P9I& zY{|dZPKVu2>fB`?eu%;Th0>#?$;VFX_ATBbYN(QZdB1qGpUSPGm?zxF7(af$=eK*I z(AC5pKJEV`8X3(`@g(HGJ?0rQ{q56Tm$*`$_o-hv=fBANhS&~`f=!p7H=bU-sXB1F zYvT5aUvDp*)caba@K0f(eMx|ye){RApK%F&%|*= + + + + + diff --git a/docs/scripts/gen-ascii-dolly.mjs b/docs/scripts/gen-ascii-dolly.mjs new file mode 100644 index 00000000..5a07feca --- /dev/null +++ b/docs/scripts/gen-ascii-dolly.mjs @@ -0,0 +1,34 @@ +// renders the dolly logo (public/apple-touch-icon.png) as ascii art +// and prints it, ready to paste into the hero.image.html
 in
+// src/content/docs/index.mdx:
+//
+//   node scripts/gen-ascii-dolly.mjs
+import { dirname, join } from "node:path";
+import { fileURLToPath } from "node:url";
+import sharp from "sharp";
+
+// monospace glyphs are roughly twice as tall as they are wide
+const COLS = 46;
+const ROWS = 23;
+const RAMP = " .:-=+*#%@";
+
+const root = join(dirname(fileURLToPath(import.meta.url)), "..");
+const png = join(root, "public", "apple-touch-icon.png");
+
+const { data, info } = await sharp(png)
+  .resize(COLS, ROWS, { fit: "fill" })
+  .ensureAlpha()
+  .raw()
+  .toBuffer({ resolveWithObject: true });
+
+const lines = [];
+for (let y = 0; y < info.height; y++) {
+  let line = "";
+  for (let x = 0; x < info.width; x++) {
+    const alpha = data[(y * info.width + x) * 4 + 3];
+    line += RAMP[Math.round((alpha / 255) * (RAMP.length - 1))];
+  }
+  lines.push(line.trimEnd());
+}
+
+console.log(lines.join("\n").replace(/^\n+|\n+$/g, ""));
diff --git a/docs/scripts/gen-sidebar-icons.mjs b/docs/scripts/gen-sidebar-icons.mjs
new file mode 100644
index 00000000..de493358
--- /dev/null
+++ b/docs/scripts/gen-sidebar-icons.mjs
@@ -0,0 +1,75 @@
+// generates src/styles/sidebar-icons.css from lucide-astro's icons.
+// sidebar entries opt in via `attrs: { "data-icon": "" }` in
+// astro.config.mjs. to add an icon, extend the map below and run:
+//
+//   node scripts/gen-sidebar-icons.mjs
+import { readFileSync, writeFileSync } from "node:fs";
+import { dirname, join } from "node:path";
+import { fileURLToPath } from "node:url";
+
+// data-icon name -> lucide-astro component
+const ICONS = {
+  rocket: "Rocket",
+  "at-sign": "AtSign",
+  globe: "Globe",
+  "life-buoy": "LifeBuoy",
+  server: "Server",
+  "hard-drive": "HardDrive",
+  workflow: "Workflow",
+  play: "Play",
+  "key-round": "KeyRound",
+  network: "Network",
+  webhook: "Webhook",
+  cable: "Cable",
+  "arrow-up-circle": "ArrowUpCircle",
+  hammer: "Hammer",
+  "git-pull-request": "GitPullRequest",
+};
+
+const root = join(dirname(fileURLToPath(import.meta.url)), "..");
+
+const svgFor = (component) => {
+  const source = readFileSync(
+    join(root, "node_modules/lucide-astro/dist", `${component}.astro`),
+    "utf8",
+  );
+  const body = source
+    .split("\n")
+    .filter((line) => line.trim().startsWith("<") && !line.includes("Layout"))
+    .map((line) => line.trim())
+    .join("");
+  return (
+    '' +
+    body +
+    ""
+  );
+};
+
+let css = `/* GENERATED by scripts/gen-sidebar-icons.mjs — do not edit by hand */
+
+#starlight__sidebar a[data-icon]::before {
+  content: "";
+  display: inline-block;
+  width: 1rem;
+  height: 1rem;
+  margin-inline-end: 0.4em;
+  vertical-align: -0.19em;
+  background-color: currentColor;
+  -webkit-mask-size: 100%;
+  mask-size: 100%;
+}
+`;
+
+for (const [name, component] of Object.entries(ICONS)) {
+  const url = `url("data:image/svg+xml,${encodeURIComponent(svgFor(component))}")`;
+  css += `
+#starlight__sidebar a[data-icon="${name}"]::before {
+  -webkit-mask-image: ${url};
+  mask-image: ${url};
+}
+`;
+}
+
+writeFileSync(join(root, "src/styles/sidebar-icons.css"), css);
+console.log(`wrote src/styles/sidebar-icons.css (${Object.keys(ICONS).length} icons)`);
diff --git a/docs/search.html b/docs/search.html
deleted file mode 100644
index 08c09342..00000000
--- a/docs/search.html
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/docs/src/assets/logotype-dark.svg b/docs/src/assets/logotype-dark.svg
new file mode 100644
index 00000000..9c53d442
--- /dev/null
+++ b/docs/src/assets/logotype-dark.svg
@@ -0,0 +1,12 @@
+
+
+    
+    
+    
+    
+    
+    
+    
+    
+    
+
diff --git a/docs/src/assets/logotype-light.svg b/docs/src/assets/logotype-light.svg
new file mode 100644
index 00000000..90f9cefc
--- /dev/null
+++ b/docs/src/assets/logotype-light.svg
@@ -0,0 +1,12 @@
+
+
+    
+    
+    
+    
+    
+    
+    
+    
+    
+
diff --git a/docs/src/components/FeatureCard.astro b/docs/src/components/FeatureCard.astro
new file mode 100644
index 00000000..fcb2c356
--- /dev/null
+++ b/docs/src/components/FeatureCard.astro
@@ -0,0 +1,23 @@
+---
+interface Props {
+  title: string;
+  href: string;
+}
+
+const { title, href } = Astro.props;
+---
+
+
+  
+    
+  
+  
+    {title}
+    
+      
+    
+  
+
diff --git a/docs/src/components/HeaderLinks.astro b/docs/src/components/HeaderLinks.astro
new file mode 100644
index 00000000..c73b70c5
--- /dev/null
+++ b/docs/src/components/HeaderLinks.astro
@@ -0,0 +1,9 @@
+---
+// overrides starlight's SocialIcons slot to add plain text links
+// before the icons; styled in styles/tangled.css
+import Default from "@astrojs/starlight/components/SocialIcons.astro";
+---
+
+tangled.org
+blog
+
diff --git a/docs/src/content.config.ts b/docs/src/content.config.ts
new file mode 100644
index 00000000..7fbcf2c3
--- /dev/null
+++ b/docs/src/content.config.ts
@@ -0,0 +1,7 @@
+import { defineCollection } from "astro:content";
+import { docsLoader } from "@astrojs/starlight/loaders";
+import { docsSchema } from "@astrojs/starlight/schema";
+
+export const collections = {
+  docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
+};
diff --git a/docs/src/content/docs/contributing/guide.md b/docs/src/content/docs/contributing/guide.md
new file mode 100644
index 00000000..43a084de
--- /dev/null
+++ b/docs/src/content/docs/contributing/guide.md
@@ -0,0 +1,136 @@
+---
+title: Contribution guide
+description: Commit guidelines, code formatting, proposals, and DCO.
+---
+
+
+## Commit guidelines
+
+We follow a commit style similar to the Go project. Please keep commits:
+
+- **atomic**: each commit should represent one logical change
+- **descriptive**: the commit message should clearly describe what the
+  change does and why it's needed
+
+### Message format
+
+```
+/: 
+
+Optional longer description can go here, if necessary. Explain what the
+change does and why, especially if not obvious. Reference relevant
+issues or PRs when applicable. These can be links for now since we don't
+auto-link issues/PRs yet.
+```
+
+Here are some examples:
+
+```
+appview/state: fix token expiry check in middleware
+
+The previous check did not account for clock drift, leading to premature
+token invalidation.
+```
+
+```
+knotserver/git/service: improve error checking in upload-pack
+```
+
+### General notes
+
+- PRs get merged "as-is" (fast-forward)—like applying a patch-series
+  using `git am`. At present, there is no squashing—so please author
+  your commits as they would appear on `master`, following the above
+  guidelines.
+- If there is a lot of nesting, for example "appview:
+  pages/templates/repo/fragments: ...", these can be truncated down to
+  just "appview: repo/fragments: ...". If the change affects a lot of
+  subdirectories, you may abbreviate to just the top-level names, e.g.
+  "appview: ..." or "knotserver: ...".
+- Keep commits lowercased with no trailing period.
+- Use the imperative mood in the summary line (e.g., "fix bug" not
+  "fixed bug" or "fixes bug").
+- Try to keep the summary line under 72 characters, but we aren't too
+  fussed about this.
+- Follow the same formatting for PR titles if filled manually.
+- Don't include unrelated changes in the same commit.
+- Avoid noisy commit messages like "wip" or "final fix"—rewrite history
+  before submitting if necessary.
+
+## Code formatting
+
+We use a variety of tools to format our code, and multiplex them with
+[`treefmt`](https://treefmt.com). All you need to do to format your changes
+is run `nix run .#fmt` (or just `treefmt` if you're in the devshell).
+
+## Proposals for bigger changes
+
+Small fixes like typos, minor bugs, or trivial refactors can be
+submitted directly as PRs.
+
+For larger changes—especially those introducing new features, significant
+refactoring, or altering system behavior—please open a proposal first. This
+helps us evaluate the scope, design, and potential impact before implementation.
+
+Create a new issue titled:
+
+```
+proposal: : 
+```
+
+In the description, explain:
+
+- What the change is
+- Why it's needed
+- How you plan to implement it (roughly)
+- Any open questions or tradeoffs
+
+We'll use the issue thread to discuss and refine the idea before moving
+forward.
+
+## Developer Certificate of Origin (DCO)
+
+We require all contributors to certify that they have the right to
+submit the code they're contributing. To do this, we follow the
+[Developer Certificate of Origin
+(DCO)](https://developercertificate.org/).
+
+By signing your commits, you're stating that the contribution is your
+own work, or that you have the right to submit it under the project's
+license. This helps us keep things clean and legally sound.
+
+To sign your commit, just add the `-s` flag when committing:
+
+```sh
+git commit -s -m "your commit message"
+```
+
+This appends a line like:
+
+```
+Signed-off-by: Your Name 
+```
+
+We won't merge commits if they aren't signed off. If you forget, you can
+amend the last commit like this:
+
+```sh
+git commit --amend -s
+```
+
+If you're submitting a PR with multiple commits, make sure each one is
+signed.
+
+For [jj](https://jj-vcs.github.io/jj/latest/) users, you can run the following command
+to make it sign off commits in the tangled repo:
+
+```shell
+# Safety check, should say "No matching config key..."
+jj config list templates.commit_trailers
+# The command below may need to be adjusted if the command above returned something.
+jj config set --repo templates.commit_trailers "format_signed_off_by_trailer(self)"
+```
+
+Refer to the [jujutsu
+documentation](https://jj-vcs.github.io/jj/latest/config/#commit-trailers)
+for more information.
diff --git a/docs/src/content/docs/contributing/hacking.md b/docs/src/content/docs/contributing/hacking.md
new file mode 100644
index 00000000..99f8bffd
--- /dev/null
+++ b/docs/src/content/docs/contributing/hacking.md
@@ -0,0 +1,169 @@
+---
+title: Hacking on Tangled
+description: Set up a dev environment and run the appview, knots, and spindles locally.
+---
+
+
+We highly recommend [installing
+Nix](https://nixos.org/download/) (the package manager)
+before working on the codebase. The Nix flake provides a lot
+of helpers to get started and most importantly, builds and
+dev shells are entirely deterministic.
+
+To set up your dev environment:
+
+```bash
+nix develop
+```
+
+Non-Nix users can look at the `devShell` attribute in the
+`flake.nix` file to determine necessary dependencies.
+
+## Running the appview
+
+The appview requires Redis and OAuth JWKs. Start these
+first, before launching the appview itself.
+
+```bash
+# OAuth JWKs should already be set up by the Nix devshell:
+echo $TANGLED_OAUTH_CLIENT_SECRET
+z42ty4RT1ovnTopY8B8ekz9NuziF2CuMkZ7rbRFpAR9jBqMc
+
+echo $TANGLED_OAUTH_CLIENT_KID
+1761667908
+
+# if not, you can set it up yourself:
+goat key generate -t P-256
+Key Type: P-256 / secp256r1 / ES256 private key
+Secret Key (Multibase Syntax): save this securely (eg, add to password manager)
+        z42tuPDKRfM2mz2Kv953ARen2jmrPA8S9LX9tRq4RVcUMwwL
+Public Key (DID Key Syntax): share or publish this (eg, in DID document)
+        did:key:zDnaeUBxtG6Xuv3ATJE4GaWeyXM3jyamJsZw3bSPpxx4bNXDR
+
+# the secret key from above
+export TANGLED_OAUTH_CLIENT_SECRET="z42tuP..."
+
+# Run Redis in a new shell to store OAuth sessions
+redis-server
+```
+
+The Nix flake exposes a few `app` attributes (run `nix
+flake show` to see a full list of what the flake provides),
+one of the apps runs the appview with the `air`
+live-reloader:
+
+```bash
+TANGLED_DEV=true nix run .#watch-appview
+
+# TANGLED_DB_PATH might be of interest to point to
+# different sqlite DBs
+
+# in a separate shell, you can live-reload tailwind
+nix run .#watch-tailwind
+```
+
+## Running knots and spindles
+
+An end-to-end knot setup requires setting up a machine with
+`sshd`, `AuthorizedKeysCommand`, and a Git user, which is
+quite cumbersome. So the Nix flake provides a
+`nixosConfiguration` to do so.
+
+
+ macOS users will have to set up a Nix Builder first + +In order to build Tangled's dev VM on macOS, you will +first need to set up a Linux Nix builder. The recommended +way to do so is to run a [`darwin.linux-builder` +VM](https://nixos.org/manual/nixpkgs/unstable/#sec-darwin-builder) +and to register it in `nix.conf` as a builder for Linux +with the same architecture as your Mac (`linux-aarch64` if +you are using Apple Silicon). + +If you're on nix-darwin, you can simply add + +``` +nix.linux-builder.enable = true; +``` + +to your host's `configuration.nix`. + +Alternatively, you can use any other method to set up a +Linux machine with Nix installed that you can `sudo ssh` +into (in other words, root user on your Mac has to be able +to ssh into the Linux machine without entering a password) +and that has the same architecture as your Mac. See +[remote builder +instructions](https://nix.dev/manual/nix/2.28/advanced-topics/distributed-builds.html#requirements) +for how to register such a builder in `nix.conf`. + +> WARNING: If you'd like to use +> [`nixos-lima`](https://github.com/nixos-lima/nixos-lima) or +> [Orbstack](https://orbstack.dev/), note that setting them up so that `sudo +ssh` works can be tricky. It seems to be [possible with +> Orbstack](https://github.com/orgs/orbstack/discussions/1669). + +
+ +To begin, grab your DID from http://localhost:3000/settings. +Then, set `TANGLED_VM_KNOT_OWNER` and +`TANGLED_VM_SPINDLE_OWNER` to your DID. You can now start a +lightweight NixOS VM like so: + +```bash +nix run --impure .#vm + +# type `poweroff` at the shell to exit the VM +``` + +This starts a knot on port 6444, a spindle on port 6555 +with `ssh` exposed on port 2222. + +Once the services are running, head to +http://localhost:3000/settings/knots and hit "Verify". It should +verify the ownership of the services instantly if everything +went smoothly. + +You can push repositories to this VM with this ssh config +block on your main machine: + +```bash +Host nixos-shell + Hostname localhost + Port 2222 + User git + IdentityFile ~/.ssh/my_tangled_key +``` + +Set up a remote called `local-dev` on a git repo: + +```bash +git remote add local-dev git@nixos-shell:user/repo +git push local-dev main +``` + +The above VM should already be running a spindle on +`localhost:6555`. Head to http://localhost:3000/settings/spindles and +hit "Verify". You can then configure each repository to use +this spindle and run CI jobs. + +Of interest when debugging spindles: + +``` +# Service logs from journald: +journalctl -xeu spindle + +# CI job logs from disk: +ls /var/log/spindle + +# Debugging spindle database: +sqlite3 /var/lib/spindle/spindle.db + +# litecli has a nicer REPL interface: +litecli /var/lib/spindle/spindle.db +``` + +If for any reason you wish to disable either one of the +services in the VM, modify [nix/vm.nix](https://tangled.org/@tangled.org/core/blob/master/nix/vm.nix) and set +`services.tangled.spindle.enable` (or +`services.tangled.knot.enable`) to `false`. diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx new file mode 100644 index 00000000..b401e9d0 --- /dev/null +++ b/docs/src/content/docs/index.mdx @@ -0,0 +1,144 @@ +--- +title: Tangled Docs +description: The next-generation social coding platform. +head: + - tag: title + content: Tangled Docs +template: splash +hero: + image: + html: | + + title: Docs and self-hosting guides for Tangled + tagline: Tangled is a decentralized code hosting and collaboration platform built on the AT Protocol. Every component is open-source and self-hostable. + actions: + - text: Quick start + link: /quick-start/ + # lucide arrow-right, same as the appview home CTAs + icon: '' + variant: primary + - text: What is Tangled? + link: "#what-is-tangled" + variant: minimal +--- + +import FeatureCard from "../../components/FeatureCard.astro"; +import { + Rocket, + Server, + Workflow, + Globe, + Webhook, + Cable, + Hammer, + GitPullRequest, + LifeBuoy, + ArrowUpCircle, +} from "lucide-astro"; + +
+ + + Create an account, add your SSH key, and push your first repository. + + + + Lightweight, headless git servers. Run one on anything from a Raspberry Pi + to a community server. + + + + CI/CD runners for your repositories. Write workflows, or self-host your own + spindle. + + + + Serve static websites directly from your git repositories. + + + + Receive HTTP notifications on repository events and integrate with external + services. + + + + A read-only XRPC API appview for Tangled records. + +
+ +
+ +## What is Tangled? + +Tangled is a social coding platform built on an [open +protocol](https://atproto.com). + +There are several models for decentralized code collaboration platforms, +ranging from ActivityPub's (Forgejo) federated model, to Radicle's +entirely P2P model. Our approach attempts to be the best of both worlds +by adopting the [AT Protocol](https://atproto.com) — a protocol for +building decentralized social applications with a central identity. + +Central to our approach is the idea of **knots**. Knots are lightweight, +headless servers that enable users to host git repositories with ease. +Knots are designed for either single or multi-tenant use, which is +perfect for self-hosting on a Raspberry Pi at home, or larger +"community" servers. By default, Tangled provides managed knots where +you can host your repositories for free. + +The appview at [tangled.org](https://tangled.org) acts as a consolidated +"view" into the whole network, allowing users to access, clone and +contribute to repositories hosted across different knots seamlessly. + +Curious about the protocol choice? Read [why AT Protocol](/why-atproto/). + +
+ +
+ +## Resources + +
+ + + Set up a dev environment and run the appview, knots, and spindles locally. + + + + Commit guidelines, code formatting, proposals, and signing your work. + + + + Upgrade guides for self-hosted knots and spindles. + + + + Common issues with login, punchcards, and commit verification. + +
+ +
diff --git a/docs/src/content/docs/knots/index.md b/docs/src/content/docs/knots/index.md new file mode 100644 index 00000000..1195b955 --- /dev/null +++ b/docs/src/content/docs/knots/index.md @@ -0,0 +1,60 @@ +--- +title: What are knots? +description: Knots are lightweight, headless git servers — the foundation of Tangled's decentralized architecture. +sidebar: + order: 0 +--- + +Knots are the foundation of Tangled's distributed architecture: +lightweight, headless servers that host git repositories. They are +designed to be trivial to run, whether that's a single-tenant +setup on a Raspberry Pi at home, or a larger multi-tenant +"community" server shared by many users. + +There are several models for decentralized code collaboration, +ranging from ActivityPub's (Forgejo) federated model to Radicle's +entirely P2P model. Tangled takes a hybrid approach by building on +the [AT Protocol](https://atproto.com): decentralized data and +hosting, with a central identity. Your repositories live on a knot +you choose (or run yourself), but your identity — and things like +issues, pull requests, stars and follows — live in your AT +Protocol account, owned by you. + +The appview at [tangled.org](https://tangled.org) ties the network +together: it acts as a consolidated "view" into repositories +hosted across all knots, so you can access, clone, and contribute +to any of them seamlessly. A repository on a home server appears +alongside one on a community knot, with no fragmentation. + +## What a knot does + +A knot: + +- serves git over SSH (pushes) and HTTP (clones, fetches) +- emits events — pushes, ref updates — that the appview and other + services consume +- owns its member list and per-repository collaborators, served + over XRPC (as of v1.15) +- assigns each repository a DID (as of v1.13), making repositories + stable across renames and transfers + +By default, Tangled provides managed knots where you can host your +repositories for free — you never have to run one yourself. But if +you want your code on your own hardware, self-hosting is a first-class +workflow. + +## Run your own + +Head to the [self-hosting guide](/knots/self-hosting/) to set one +up — there's a NixOS module, a Docker Compose setup, and a manual +installation path. If you run into trouble, check the +[troubleshooting section](/knots/self-hosting/#troubleshooting), +and keep an eye on the [migration guides](/reference/migrations/) +when upgrading. + +## Further reading + +- [Introducing Tangled](https://blog.tangled.org/intro) — the + original announcement, with more on the thinking behind knots +- [Hacking on Tangled](/contributing/hacking/) — run a knot + locally in a dev VM diff --git a/docs/src/content/docs/knots/self-hosting.md b/docs/src/content/docs/knots/self-hosting.md new file mode 100644 index 00000000..5790e7c7 --- /dev/null +++ b/docs/src/content/docs/knots/self-hosting.md @@ -0,0 +1,375 @@ +--- +title: Self-hosting a knot +description: "Run your own knot: a lightweight, headless git server." +--- + + +So you want to run your own knot server? Great! Here are a few prerequisites: + +1. A server of some kind (a VPS, a Raspberry Pi, etc.). Preferably running a Linux distribution of some kind. +2. A (sub)domain name. People generally use `knot.example.com`. +3. A valid SSL certificate for your domain. + +## NixOS + +Refer to the [knot +module](https://tangled.org/tangled.org/core/blob/master/nix/modules/knot.nix) +for a full list of options. Sample configurations: + +- [The test VM](https://tangled.org/tangled.org/core/blob/master/nix/vm.nix#L85) +- [@pyrox.dev/nix](https://tangled.org/pyrox.dev/nix/blob/c2b644c214d278af12523618de952ee2eab1af3d/hosts/marvin/services/tangled.nix#L15-26) + +## Docker + +Refer to +[@tangled.org/knot-docker](https://tangled.org/@tangled.org/knot-docker). +Note that this is community maintained. + +## Manual setup + +First, clone this repository: + +``` +git clone https://tangled.org/@tangled.org/core +``` + +Then, build the `knot` CLI. This is the knot administration +and operation tool. For the purpose of this guide, we're +only concerned with these subcommands: + +- `knot server`: the main knot server process, typically + run as a supervised service +- `knot guard`: handles role-based access control for git + over SSH (you'll never have to run this yourself) +- `knot keys`: fetches SSH keys associated with your knot; + we'll use this to generate the SSH + `AuthorizedKeysCommand` + +``` +cd core +export CGO_ENABLED=1 +go build -o knot ./cmd/knot +``` + +Next, move the `knot` binary to a location owned by `root` -- +`/usr/local/bin/` is a good choice. Make sure the binary itself is also owned by `root`: + +``` +sudo mv knot /usr/local/bin/knot +sudo chown root:root /usr/local/bin/knot +``` + +This is necessary because SSH `AuthorizedKeysCommand` requires [really +specific permissions](https://stackoverflow.com/a/27638306). The +`AuthorizedKeysCommand` specifies a command that is run by `sshd` to +retrieve a user's public SSH keys dynamically for authentication. Let's +set that up. + +``` +sudo tee /etc/ssh/sshd_config.d/authorized_keys_command.conf < /home/git/motd +``` + +Note that you should add a newline at the end if setting a non-empty message +since the knot won't do this for you. + +## Secure Mode + +Secure Mode isolates each `git` subprocess to the repository it is +operating on, using two mechanisms: + +- **Linux Landlock** restricts the filesystem paths the subprocess + can access -- it can only read/write its own repository and the + system directories it needs to run. +- **UID isolation** runs each subprocess as a virtual UID assigned + to the repository owner, so that repositories belonging to + different owners are isolated from each other at the OS level + even if Landlock were somehow bypassed. + +Secure Mode requires: + +- Linux kernel >= 5.19 (Landlock V2). This is the minimum needed + for `git push` to work, because receive-pack's quarantine + migration uses cross-directory rename which requires the + Landlock `REFER` access right (added in V2). Kernels 5.13-5.18 + support Landlock V1 and clones will work, but pushes will fail + with cross-device link errors. On kernels without any Landlock + support (< 5.13), the sandbox call is a no-op: UID isolation + still applies but no filesystem restriction is enforced. +- `CAP_SETUID`, `CAP_SETGID`, and `CAP_CHOWN` available to the + knot process. The NixOS module grants these automatically; for + manual setups see the `setcap` step below. + +### NixOS + +Add `server.secureMode = true;` to your knot module configuration: + +```nix +services.tangled.knot = { + server.secureMode = true; + # ... other options +}; +``` + +The NixOS module handles everything else automatically: + +- Grants the required capabilities to the knot service via + `AmbientCapabilities` in the systemd unit. +- Installs a capability-bearing wrapper at + `/run/wrappers/bin/knot` via `security.wrappers`, so that + SSH-invoked git operations (pushes) also run under the correct + UID without requiring the service to run as root. +- Runs `knot migrate-isolation` at service start to chown + existing repositories to their virtual UIDs. + +### Manual setup + +**Step 1.** Grant the required capabilities to the knot binary. +This allows the knot process to switch to virtual UIDs at runtime +without running as root. You will need to repeat this step +whenever the binary is updated. + +``` +sudo setcap cap_setuid,cap_setgid,cap_chown+eip /usr/local/bin/knot +``` + +**Step 2.** Run the migration tool to assign virtual UIDs to all +existing repositories and set their filesystem permissions. This +must be run as root: + +``` +sudo knot migrate-isolation \ + --git-dir /home/git \ + --db /home/git/knotserver.db \ + --internal-api 127.0.0.1:5444 +``` + +You can re-run this at any time with `--force` to reapply +permissions (e.g. after a manual repair or after updating the +binary). + +**Step 2a.** Ensure the home directory is traversable by +non-group users. Git subprocesses run as virtual UIDs that are +not in the git group, and they need to resolve +`$HOME/.config/git/config` to load the global config: + +``` +sudo chmod o+x /home/git +``` + +This adds only the execute bit, not read -- the virtual UIDs can +traverse to known paths but cannot list directory contents. + +**Step 3.** Enable Secure Mode in your environment file: + +``` +KNOT_SERVER_SECURE_MODE=true +``` + +Or pass it as a flag: + +``` +knot server --secure-mode +``` + +**Step 4.** Regenerate the `AuthorizedKeysCommand` with the +`-secure-mode` flag. This causes `knot keys` to emit guard +command lines that include `-secure-mode`, so SSH pushes also +get UID isolation: + +``` +sudo tee /etc/ssh/sshd_config.d/authorized_keys_command.conf < **Note:** the server will refuse to start in Secure Mode if any +> repositories have not yet been isolation-migrated. Re-run +> `migrate-isolation` if you see this error. + +## Troubleshooting + +If you run your own knot, you may run into some of these +common issues. You can always join the +[IRC](https://web.libera.chat/#tangled) or +[Discord](https://chat.tangled.org/) if this section does +not help. + +### Unable to push + +If you are unable to push to your knot or repository: + +1. First, ensure that you have added your SSH public key to + your account +2. Check to see that your knot has synced the key by running + `knot keys` +3. Check to see if git is supplying the correct private key + when pushing: `GIT_SSH_COMMAND="ssh -v" git push ...` +4. Check to see if `sshd` on the knot is rejecting the push + for some reason: `journalctl -xeu ssh` (or `sshd`, + depending on your machine). These logs are unavailable if + using docker. +5. Check to see if the knot itself is rejecting the push, + depending on your setup, the logs might be in one of the + following paths: + - `/tmp/knotguard.log` + - `/home/git/log` + - `/home/git/guard.log` diff --git a/docs/src/content/docs/quick-start.md b/docs/src/content/docs/quick-start.md new file mode 100644 index 00000000..58d97d2b --- /dev/null +++ b/docs/src/content/docs/quick-start.md @@ -0,0 +1,251 @@ +--- +title: Quick start +description: Create an account, add an SSH key, and push your first repository to Tangled. +--- + + +## Login or sign up + +You can [login](https://tangled.org) by using your AT Protocol +account. If you are unclear on what that means, simply head +to the [signup](https://tangled.org/signup) page and create +an account. By doing so, you will be choosing Tangled as +your account provider (you will be granted a handle of the +form `user.tngl.sh`). + +In the AT Protocol network, users are free to choose their account +provider (known as a "Personal Data Service", or PDS), and +login to applications that support AT accounts. + +You can think of it as "one account for all of the atmosphere"! + +If you already have an AT account (you may have one if you +signed up to Bluesky, for example), you can login with the +same handle on Tangled (so just use `user.bsky.social` on +the login page). + +## Add an SSH key + +Once you are logged in, you can start creating repositories +and pushing code. Tangled supports pushing git repositories +over SSH. + +First, you'll need to generate an SSH key if you don't +already have one: + +```bash +ssh-keygen -t ed25519 -C "foo@bar.com" +``` + +When prompted, save the key to the default location +(`~/.ssh/id_ed25519`) and optionally set a passphrase. + +Copy your public key to your clipboard: + +```bash +# on X11 +cat ~/.ssh/id_ed25519.pub | xclip -sel c + +# on wayland +cat ~/.ssh/id_ed25519.pub | wl-copy + +# on macos +cat ~/.ssh/id_ed25519.pub | pbcopy +``` + +Now, navigate to 'Settings' -> 'Keys' and hit 'Add Key', +paste your public key, give it a descriptive name, and hit +save. + +## Create a repository + +Once your SSH key is added, create your first repository: + +1. Hit the green `+` icon on the topbar, and select + repository +2. Enter a repository name +3. Add a description +4. Choose a knotserver to host this repository on +5. Hit create + +Knots are self-hostable, lightweight Git servers that can +host your repository. Unlike traditional code forges, your +code can live on any server. Read the [Knots](/knots/self-hosting/) section +for more. + +## Configure SSH + +To ensure Git uses the correct SSH key and connects smoothly +to Tangled, add this configuration to your `~/.ssh/config` +file: + +``` +Host tangled.org + Hostname tangled.org + User git + IdentityFile ~/.ssh/id_ed25519 + AddressFamily inet +``` + +This tells SSH to use your specific key when connecting to +Tangled and prevents authentication issues if you have +multiple SSH keys. + +Note that this configuration only works for knotservers that +are hosted by tangled.org. If you use a custom knot, refer +to the [Knots](/knots/self-hosting/) section. + +## Push your first repository + +Initialize a new Git repository: + +```bash +mkdir my-project +cd my-project + +git init +echo "# My Project" > README.md +``` + +Add some content and push! + +```bash +git add README.md +git commit -m "Initial commit" +git remote add origin git@tangled.org:user.tngl.sh/my-project +git push -u origin main +``` + +That's it! Your code is now hosted on Tangled. + +## Migrating an existing repository + +Moving your repositories from GitHub, GitLab, Bitbucket, or +any other Git forge to Tangled is straightforward. You'll +simply change your repository's remote URL. At the moment, +Tangled does not have any tooling to migrate data such as +GitHub issues or pull requests. + +First, create a new repository on tangled.org as described +in the [Quick Start Guide](#create-a-repository). + +Navigate to your existing local repository: + +```bash +cd /path/to/your/existing/repo +``` + +You can inspect your existing Git remote like so: + +```bash +git remote -v +``` + +You'll see something like: + +```bash +origin git@github.com:username/my-project.git (fetch) +origin git@github.com:username/my-project.git (push) +``` + +Update the remote URL to point to tangled: + +```bash +git remote set-url origin git@tangled.org:user.tngl.sh/my-project +``` + +Verify the change: + +```bash +git remote -v +``` + +You should now see: + +```bash +origin git@tangled.org:user.tngl.sh/my-project (fetch) +origin git@tangled.org:user.tngl.sh/my-project (push) +``` + +Push all your branches and tags to Tangled: + +```bash +git push -u origin --all +git push -u origin --tags +``` + +Your repository is now migrated to Tangled! All commit +history, branches, and tags have been preserved. + +## Mirroring a repository to Tangled + +If you want to maintain your repository on multiple forges +simultaneously, for example, keeping your primary repository +on GitHub while mirroring to Tangled for backup or +redundancy, you can do so by adding [multiple remotes](https://git-scm.com/docs/git-push#_remotes). + +You can configure your local repository to push to both +Tangled and, say, GitHub. You may already have the following +setup: + +```bash +$ git remote -v +origin git@github.com:username/my-project.git (fetch) +origin git@github.com:username/my-project.git (push) +``` + +Now add Tangled as an additional push URL to the same +remote: + +```bash +git remote set-url --add --push origin git@tangled.org:user.tngl.sh/my-project +``` + +You also need to re-add the original URL as a push +destination (Git will now use the original URL to fetch only): + +```bash +git remote set-url --add --push origin git@github.com:username/my-project.git +``` + +Verify your configuration: + +```bash +$ git remote -v +origin git@github.com:username/my-project.git (fetch) +origin git@tangled.org:user.tngl.sh/my-project (push) +origin git@github.com:username/my-project.git (push) +``` + +Notice that there's one fetch URL (the primary remote) and +two push URLs. Now, whenever you push, Git will +automatically push to both remotes: + +```bash +git push origin main +``` + +This single command pushes your `main` branch to both GitHub +and Tangled simultaneously. + +To push all branches and tags: + +```bash +git push origin --all +git push origin --tags +``` + +If you prefer more control over which remote you push to, +you can maintain separate remotes: + +```bash +git remote add github git@github.com:username/my-project.git +git remote add tangled git@tangled.org:user.tngl.sh/my-project +``` + +Then push to each explicitly: + +```bash +git push github main +git push tangled main +``` diff --git a/docs/src/content/docs/reference/bobbin.md b/docs/src/content/docs/reference/bobbin.md new file mode 100644 index 00000000..e7396072 --- /dev/null +++ b/docs/src/content/docs/reference/bobbin.md @@ -0,0 +1,239 @@ +--- +title: Bobbin +description: A read-only XRPC API appview for Tangled records. +--- + + +Bobbin is an API appview for Tangled records. It serves XRPC +endpoints for `sh.tangled.*`, with it you can get repos, +issues, pulls, comments, follows, stars, labels, pipelines, +and profiles. It is read-only, there is no auth, since that +should all be handled direct-to-PDS and knot respectively. + +**Bobbin has no permanent storage**. + +It is only a glorified edge index, in the graph theory +sense. Additionally it has a record cache, re-filled on +demand. All other data that Bobbin serves comes live from +PDSes & knots. + +## What Bobbin needs + +The way that Bobbin is able to pull off being +so stateless is by moving state upstream. +Primarily it depends on an instance of +[Hydrant](https://tangled.org/did:plc:6v3ul2ptnqctyxwkz5ti4amn) +, which is the service that gives an event stream +for Bobbin to quickly backfill from on every restart. +Backfilling ought to take less than a couple of minutes +maximum. If the upstream instance of Hydrant fails +while Bobbin is live, its list/count endpoints stop +advancing and report a stale cursor. Single-lookups +will continue working, due to the second dependency: +[Slingshot](https://tangled.org/did:plc:c7mc2fn47ihdihul4vjwsuy3/tree/main/slingshot). +Slingshot fetches individual records & resolves identities. +If the upstream instance of Slingshot fails, single-lookups +will fail with a `502` error. There are some aggregation +endpoints that use Slingshot for hydrating, which will also +fail. + +A soft dependency that ought to exist for Bobbin to operate +correctly is simply the plethora of knots that are out +there, that Bobbin talks to directly for git data and, for +knots at v1.15+, members & collaborators. + +## Building Bobbin + +Bobbin is under [Tangled's core monorepo, under bobbin/](https://tangled.org/did:plc:j5hmlfdrwkvtxm7cjmu7j2is/tree/master/bobbin). +Here's an easy local debug-build: + +```sh +cargo build -p bobbin +``` + +Bobbin loves being in a container. When using +`bobbin/containerfiles/bobbin.Containerfile`, it runs `cargo +build --release --bin bobbin --package bobbin` within a +little Debian runtime, exposing port 8090. + +## Configuration + +The best way to configure Bobbin is via a toml config file. +There's an `example.toml` in [Bobbin's subdir](https://tangled.org/did:plc:j5hmlfdrwkvtxm7cjmu7j2is/blob/master/bobbin/example.toml). +Every value is overridable by a `BOBBIN_*` env var. +The load order is env, then `--config `, then +`/etc/bobbin/config.toml`, then built-in defaults. + +Load and check a config without starting the server: + +```sh +bobbin --config config.toml validate +``` + +Minimal config is the two upstream URLs. The hydrant URL +takes `ws://` or `wss://`. An `http://` or `https://` +URL is rewritten to the matching websocket scheme at +connection-time. + +```toml +[server] +binds = ["127.0.0.1:8090"] + +# Loopback-only & can leave empty to disable debug introspection. +debug_bind = "127.0.0.1:8091" + +[hydrant] +url = "https://hydrant.example.com" + +[slingshot] +url = "https://slingshot.example.com" +``` + +> 🦪 Lewis +> +> At time of writing, we (Tangled) don't host public +> instances of Hydrant or Slingshot. You will have to +> find public instances or spin these up yourself! :P + +Take a gander in the project's example.toml for an +exhaustive list of things to configure. + +You will discover fun things such as a configurable adaptive +loop that watches the cgroup memory limit & throttles heavy +requests under pressure. It only works if it detects a +cgroup limit is present. The config for that is in the +`[backpressure]` block of the config template. + +## Running Bobbin + +Start the server using a config toml: + +```bash +bobbin --config config.toml +``` +Bobbin wakes up in a cold sweat and immediately gets to +work: +1. It binds its listeners, connects to the Hydrant stream + in the background. +2. It serves requests from the first + moment it's alive, even before the Hydrant stream connects + or finishes catching up. Having a cold Hydrant itself + costs only latency and approximate counts. + +## The API + +**Single lookups** take a record's AT-URI. + +- `getRepo` takes the repo URI: + +```sh +curl "$BOBBIN/xrpc/sh.tangled.repo.getRepo?repo=at://did:plc:boltless/sh.tangled.repo/squid" +``` +```json +{ + "uri": "at://did:plc:boltless/sh.tangled.repo/squid", + "cid": "bafyrei...", + "value": { "$type": "sh.tangled.repo", "knot": "knot1.tangled.sh", "description": "...", "createdAt": "..." } +} +``` + +- `getProfile` takes the full profile record URI, so a bare + handle or DID will not resolve: + +```sh +curl "$BOBBIN/xrpc/sh.tangled.actor.getProfile?actor=at://did:plc:boltless/sh.tangled.actor.profile/self" +``` + +- If Slingshot cannot serve the record, the response is `502`: + +```json +{ "error": "UpstreamFailed", "message": "upstream unavailable: ..." } +``` + +**Aggregation** endpoints come in `list*` and `count*` pairs, +each with a `*By` sibling, and require a `subject` query param. + +- `listRepos` and `countRepos` key on the owner DID: + +```sh +curl "$BOBBIN/xrpc/sh.tangled.repo.countRepos?subject=did:plc:boltless" +``` +```json +{ "count": 7, "distinctAuthors": 1 } +``` + +```sh +curl "$BOBBIN/xrpc/sh.tangled.repo.listRepos?subject=did:plc:boltless&limit=3" +``` +```json +{ "items": [ { "uri": "at://did:plc:boltless/sh.tangled.repo/squid", "cid": "bafyrei...", "value": { } } ], "cursor": null } +``` + +- Bobbin validates the subject per collection. Here a repo URI + is passed where a bare DID is required, so the call returns a + `400`: + +```sh +curl "$BOBBIN/xrpc/sh.tangled.graph.listFollows?subject=at://did:plc:boltless/sh.tangled.repo/squid" +``` +```json +{ "error": "InvalidRequest", "message": "invalid request: subject must be a bare did, got at-uri with collection sh.tangled.repo" } +``` + +**Search** is a single endpoint over an in-mem full-text +index: + +```sh +curl "$BOBBIN/xrpc/sh.tangled.search.query?q=tangled&limit=2" +``` +```json +{ "hits": [ { "uri": "at://...", "cid": "...", "nsid": "sh.tangled.repo", "score": 27.1, "value": { } } ], "cursor": null } +``` + +**Git data** such as blob, tree, diff, log, and archive proxies +straight to the repo's knot, streamed back without caching. + +## Coverage and warm-up + +- While the edge index is catching up from Hydrant, + the aggregation count is a lower bound & may still climb. +- One endpoint reports how far along the backfill it is: + +```sh +curl "$BOBBIN/xrpc/sh.tangled.bobbin.getCoverage" +``` + +While warming up: + +```json +{ "ready": false, "eventsProcessed": 45588, "lastCursor": 51658 } +``` + +Once caught up, Bobbin flips to ready: + +```json +{ "ready": true, "eventsProcessed": 106085, "lastCursor": 116527 } +``` + +If starting up Hydrant for the first time, Hydrant itself +will take a decent while (a couple of hours) to backfill +from PDSes. Hydrant stores its backfill on disk. Bobbin +restart reaches `ready` in minutes by replaying event from +an already-populated Hydrant. If your Hydrant is new, expect +Bobbin to backfill in that same couple of hours that Hydrant +takes. + +## Loose ends and not-gonna-impl + +- **No coverage signal for per-knot rosters yet.** + Coverage tracks the hydrant stream only. A v1.15 knot + that is unreachable serves a stale or empty member set + with nothing to flag it. +- **Knot eventstream fan-out isn't pooled.** + Bobbin opens one websocket per v1.15 + knot on top of the hydrant subscription. A network with + thousands of knots wants pooling or a shared subscription. +- **No sequential issue or PR numbers.** bobbin returns rkeys, + not `#42` style ids like the web appview. A client + deriving a display number does it from creation order. But + why bother? rkeys are the IDs. diff --git a/docs/src/content/docs/reference/migrations.md b/docs/src/content/docs/reference/migrations.md new file mode 100644 index 00000000..70ff4f17 --- /dev/null +++ b/docs/src/content/docs/reference/migrations.md @@ -0,0 +1,138 @@ +--- +title: Migrating knots and spindles +description: Upgrade guides for non-backwards compatible knot/spindle changes. +--- + + +Sometimes, non-backwards compatible changes are made to the +knot/spindle XRPC APIs. If you host a knot or a spindle, you +will need to follow this guide to upgrade. Typically, this +only requires you to deploy the newest version. + +This document is laid out in reverse-chronological order. +Newer migration guides are listed first, and older guides +are further down the page. + +## Upgrading to v1.16.0-alpha + +Starting with v1.16.0-alpha, spindles own CI pipeline data +directly. The appview no longer stores pipeline runs or follows +spindle event streams for pipeline history. Instead, it asks the +configured spindle for pipeline lists, single pipeline details, +workflow logs, retries, and cancellations over XRPC. + +This means that existing pipeline logs / runs won't appear after +you upgrade. Existing pipeline history from the appview cannot be +automatically migrated. If you want to migrate your data, you can +reach out to us and we will send you an SQL file that'll add the data +into your spindle. + +- Upgrade to the latest tag (v1.16.0 or above) +- Head to the [spindle + dashboard](https://tangled.org/settings/spindles) and hit the + "retry" button to verify your spindle + +## Upgrading to v1.15.0-alpha + +With v1.15.0-alpha, a knot itself owns its members and +per-repo collaborators directly. Previously this data was sourced from +PDS records (`sh.tangled.knot.member` and `sh.tangled.repo.collaborator`) +that the appview and the knot both read off the firehose. +The knot is now the source of truth and serves them over XRPC instead: + +- `sh.tangled.knot.addMember`, `sh.tangled.knot.removeMember`, `sh.tangled.knot.listMembers` +- `sh.tangled.repo.addCollaborator`, `sh.tangled.repo.removeCollaborator`, `sh.tangled.repo.listCollaborators` + +Until your knot is upgraded, the appview keeps reading its +members and collaborators from the old firehose-sourced records. +Upgrade to move your knot onto knot-owned access control. + +- Upgrade to the latest tag (v1.15.0 or above) +- Head to the [knot dashboard](https://tangled.org/settings/knots) and + hit the "retry" button to verify your knot + +## Upgrading to v1.14.0-alpha + +Starting with v1.14.0-alpha, the fully knot uses the repoDID as its +canonical handle for repositories. This unlocks repository +renames from the appview UI and changes the wire format for +the following lexicons (`sh.tangled.repo.pull`, `sh.tangled.repo.collaborator`, +`sh.tangled.repo.issue`, `sh.tangled.git.refUpdate`). + +Knots that have not been upgraded may silently drop new push +events, pull requests, issues, and collaborator invites for +repositories they host until upgraded. So upgrade please!!! + +- Upgrade to the latest tag (v1.14.0 or above) +- Head to the [knot dashboard](https://tangled.org/settings/knots) and + hit the "retry" button to verify your knot + +## Upgrading to v1.13.0-alpha + +Starting with v1.13.0-alpha, every repository on a knot is +assigned a DID. This makes repositories stable across +renames and transfers. + +When you upgrade your knot to this version, the server will +automatically mint DIDs for all existing repositories on +startup. This is a one-time process and you may see +additional log output during the first boot as DIDs are +assigned. + +- Upgrade to the latest tag (v1.13.0 or above) +- Head to the [knot dashboard](https://tangled.org/settings/knots) and + hit the "retry" button to verify your knot + +## Upgrading from v1.8.x + +After v1.8.2, the HTTP API for knots and spindles has been +deprecated and replaced with XRPC. Repositories on outdated +knots will not be viewable from the appview. Upgrading is +straightforward however. + +For knots: + +- Upgrade to the latest tag (v1.9.0 or above) +- Head to the [knot dashboard](https://tangled.org/settings/knots) and + hit the "retry" button to verify your knot + +For spindles: + +- Upgrade to the latest tag (v1.9.0 or above) +- Head to the [spindle + dashboard](https://tangled.org/settings/spindles) and hit the + "retry" button to verify your spindle + +## Upgrading from v1.7.x + +After v1.7.0, knot secrets have been deprecated. You no +longer need a secret from the appview to run a knot. All +authorized commands to knots are managed via [Inter-Service +Authentication](https://atproto.com/specs/xrpc#inter-service-authentication-jwt). +Knots will be read-only until upgraded. + +Upgrading is quite easy, in essence: + +- `KNOT_SERVER_SECRET` is no more, you can remove this + environment variable entirely +- `KNOT_SERVER_OWNER` is now required on boot, set this to + your DID. You can find your DID in the + [settings](https://tangled.org/settings) page. +- Restart your knot once you have replaced the environment + variable +- Head to the [knot dashboard](https://tangled.org/settings/knots) and + hit the "retry" button to verify your knot. This simply + writes a `sh.tangled.knot` record to your PDS. + +If you use the nix module, simply bump the flake to the +latest revision, and change your config block like so: + +```diff + services.tangled.knot = { + enable = true; + server = { +- secretFile = /path/to/secret; ++ owner = "did:plc:foo"; + }; + }; +``` diff --git a/docs/src/content/docs/reference/webhooks.md b/docs/src/content/docs/reference/webhooks.md new file mode 100644 index 00000000..189066eb --- /dev/null +++ b/docs/src/content/docs/reference/webhooks.md @@ -0,0 +1,215 @@ +--- +title: Webhooks +description: Receive HTTP POST notifications when events occur in your repositories. +--- + + +Webhooks allow you to receive HTTP POST notifications when events occur in your repositories. This enables you to integrate Tangled with external services, trigger CI/CD pipelines, send notifications, or automate workflows. + +## Overview + +Webhooks send HTTP POST requests to URLs you configure whenever specific events happen. Currently, Tangled supports push, repository rename, and pull request events, with more event types coming soon. + +## Configuring webhooks + +To set up a webhook for your repository: + +1. Navigate to your repository +2. Go to **Settings → Hooks** +3. Click **new webhook** +4. Configure your webhook: + - **Payload URL**: The endpoint that will receive the webhook POST requests + - **Secret**: An optional secret key for verifying webhook authenticity (leave blank to send unsigned webhooks) + - **Events**: Select which events trigger the webhook + - **Active**: Toggle whether the webhook is enabled + +## Webhook payload + +### Push + +When a push event occurs, Tangled sends a POST request with a JSON payload of the format: + +```json +{ + "after": "7b320e5cbee2734071e4310c1d9ae401d8f6cab5", + "before": "c04ddf64eddc90e4e2a9846ba3b43e67a0e2865e", + "pusher": { + "did": "did:plc:hwevmowznbiukdf6uk5dwrrq" + }, + "ref": "refs/heads/main", + "repository": { + "clone_url": "https://tangled.org/did:plc:hwevmowznbiukdf6uk5dwrrq/some-repo", + "created_at": "2025-09-15T08:57:23Z", + "description": "an example repository", + "fork": false, + "full_name": "did:plc:hwevmowznbiukdf6uk5dwrrq/some-repo", + "html_url": "https://tangled.org/did:plc:hwevmowznbiukdf6uk5dwrrq/some-repo", + "name": "some-repo", + "open_issues_count": 5, + "owner": { + "did": "did:plc:hwevmowznbiukdf6uk5dwrrq" + }, + "ssh_url": "ssh://git@tangled.org/did:plc:hwevmowznbiukdf6uk5dwrrq/some-repo", + "stars_count": 1, + "updated_at": "2025-09-15T08:57:23Z" + } +} +``` + +### Pull request + +Pull request events are sent as separate event types, so you can subscribe to +exactly the transitions you care about: + +- `pull_request:created` — a pull request was opened +- `pull_request:resubmitted` — a new round (revision) was pushed to a pull request +- `pull_request:merged` — a pull request was merged +- `pull_request:closed` — a pull request was closed +- `pull_request:reopened` — a closed pull request was reopened + +All pull request events share the same payload format: + +```json +{ + "action": "created", + "pull_request": { + "number": 4, + "title": "add dark mode", + "body": "implements dark mode as discussed in #2", + "state": "open", + "target_branch": "main", + "source": { + "branch": "dark-mode", + "sha": "7b320e5cbee2734071e4310c1d9ae401d8f6cab5" + }, + "round_number": 0, + "owner": { + "did": "did:plc:hwevmowznbiukdf6uk5dwrrq" + }, + "html_url": "https://tangled.org/did:plc:hwevmowznbiukdf6uk5dwrrq/some-repo/pulls/4", + "patch_url": "https://tangled.org/did:plc:hwevmowznbiukdf6uk5dwrrq/some-repo/pulls/4/round/0.patch", + "created_at": "2025-09-15T08:57:23Z" + }, + "repository": { ... }, + "sender": { + "did": "did:plc:hwevmowznbiukdf6uk5dwrrq" + } +} +``` + +Notes: + +- `action` mirrors the event type suffix (`created`, `resubmitted`, `merged`, `closed`, `reopened`). +- `repository` has the same format as in the push payload. +- The patch itself is not embedded in the payload (patches can be large); fetch it from `patch_url` instead. `round_number` identifies the latest round, and `patch_url` always points at that round's patch. +- `source` is only present for branch-based and fork-based pull requests; it is omitted for patch-based pulls. For fork-based pulls, `source.repo` contains the DID of the source repository. +- `sender` is the user who performed the action. + +## HTTP headers + +Each webhook request includes the following headers: + +- `Content-Type: application/json` +- `User-Agent: Tangled-Hook/` — User agent with short SHA of the commit (push events); `Tangled-Hook/pull_request` for pull request events +- `X-Tangled-Event: push` — The full event type (e.g. `push`, `pull_request:merged`) +- `X-Tangled-Hook-ID: ` — The webhook ID +- `X-Tangled-Delivery: ` — Unique delivery ID +- `X-Tangled-Signature-256: sha256=` — HMAC-SHA256 signature (if secret configured) + +## Verifying webhook signatures + +If you configured a secret, you should verify the webhook signature to ensure requests are authentic. For example, in Go: + +```go +package main + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "io" + "net/http" + "strings" +) + +func verifySignature(payload []byte, signatureHeader, secret string) bool { + // Remove 'sha256=' prefix from signature header + signature := strings.TrimPrefix(signatureHeader, "sha256=") + + // Compute expected signature + mac := hmac.New(sha256.New, []byte(secret)) + mac.Write(payload) + expected := hex.EncodeToString(mac.Sum(nil)) + + // Use constant-time comparison to prevent timing attacks + return hmac.Equal([]byte(signature), []byte(expected)) +} + +func webhookHandler(w http.ResponseWriter, r *http.Request) { + // Read the request body + payload, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, "Bad request", http.StatusBadRequest) + return + } + + // Get signature from header + signatureHeader := r.Header.Get("X-Tangled-Signature-256") + + // Verify signature + if signatureHeader != "" && verifySignature(payload, signatureHeader, yourSecret) { + // Webhook is authentic, process it + processWebhook(payload) + w.WriteHeader(http.StatusOK) + } else { + http.Error(w, "Invalid signature", http.StatusUnauthorized) + } +} +``` + +## Delivery retries + +Webhooks are automatically retried on failure: + +- **3 total attempts** (1 initial + 2 retries) +- **Exponential backoff** starting at 1 second, max 10 seconds +- **Retried on**: + - Network errors + - HTTP 5xx server errors +- **Not retried on**: + - HTTP 4xx client errors (bad request, unauthorized, etc.) + +### Timeouts + +Webhook requests timeout after 30 seconds. If your endpoint needs more time: + +1. Respond with 200 OK immediately +2. Process the webhook asynchronously in the background + +## Example integrations + +### Discord notifications + +```javascript +app.post("/webhook", (req, res) => { + const payload = req.body; + + fetch("https://discord.com/api/webhooks/...", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + content: `New push to ${payload.repository.full_name}`, + embeds: [ + { + title: `${payload.pusher.did} pushed to ${payload.ref}`, + url: payload.repository.html_url, + color: 0x00ff00, + }, + ], + }), + }); + + res.status(200).send("OK"); +}); +``` + diff --git a/docs/src/content/docs/sites.md b/docs/src/content/docs/sites.md new file mode 100644 index 00000000..2adce51c --- /dev/null +++ b/docs/src/content/docs/sites.md @@ -0,0 +1,105 @@ +--- +title: Hosting websites +description: Serve static websites directly from your git repositories on Tangled. +--- + + +You can serve static websites directly from your git repositories on +Tangled. If you've used GitHub Pages or Codeberg Pages, this should feel +familiar. + +## Overview + +Every user gets a sites domain. If you signed up through Tangled's own +PDS (`tngl.sh`), your sites domain is automatically +`.tngl.sh` no setup needed. Otherwise, you can claim a +`.tngl.io` domain from your settings. + +You can serve multiple sites per domain: + +- One **index site** served at the root of your domain (e.g. + `alice.tngl.sh`) +- Any number of **sub-path sites** served under the repository name + (e.g. `alice.tngl.sh/my-project`) + +## Claiming a domain + +If you don't have a `tngl.sh` handle, you need to claim a domain before +publishing sites: + +1. Go to **Settings → Sites** +2. Enter a subdomain (e.g. `alice` to claim `alice.tngl.io`) +3. Click **claim** + +You can only hold one domain at a time. Releasing a domain puts it in a +30-day cooldown before anyone else can claim it. + +## Configuring a site for a repository + +1. Navigate to your repository +2. Go to **Settings → Sites** +3. Choose a **branch** to deploy from +4. Set the **deploy directory** — the path within the repository + containing your `index.html`. Use `/` for the root, or a subdirectory + like `/docs` or `/public` +5. Choose the **site type**: + - **Index site** — served at the root of your domain (e.g. + `alice.tngl.sh`) + - **Sub-path site** — served under the repository name (e.g. + `alice.tngl.sh/my-project`) +6. Click **save** + +The site will be deployed automatically. You can see the status of your +previous deploys in the **Recent Deploys** section at the bottom of the +page. + +Sites are redeployed automatically on every push to the configured +branch. + +## Custom domains + +Tangled currently doesn't support custom domains for sites. This will be +added in a future update. + +## Deploy directory + +The deploy directory is the path within your repository that Tangled +serves as the site root. It must contain an `index.html`. + +| Deploy directory | Result | +|---|---| +| `/` | Serves the repository root | +| `/docs` | Serves the `docs/` subdirectory | +| `/public` | Serves the `public/` subdirectory | + +Directories are served with automatic `index.html` resolution -- a +request to `/about` will serve `/about/index.html` if it exists. + +## Site types + +| Type | URL | +|---|---| +| Index site | `alice.tngl.sh` | +| Sub-path site | `alice.tngl.sh/my-project` | + +Only one repository can be the index site for a given domain at a time. +If another repository already holds the index site, you will see a +notice in the settings and only the sub-path option will be available. + +## Deploy triggers + +A deployment is triggered automatically when: + +- You push to the configured branch +- You change the site configuration (branch, deploy directory, or site + type) + +## Disabling a site + +To stop serving a site, go to **Settings → Sites** in your repository +and click **Disable**. This removes the site configuration and stops +serving the site. The deployed files are also deleted from storage. + +Releasing your domain from **Settings → Sites** at the account level +will disable all sites associated with it and delete their files. + diff --git a/docs/src/content/docs/spindles/architecture.md b/docs/src/content/docs/spindles/architecture.md new file mode 100644 index 00000000..d16d250f --- /dev/null +++ b/docs/src/content/docs/spindles/architecture.md @@ -0,0 +1,37 @@ +--- +title: Architecture +description: How spindle listens for records and executes pipelines. +--- + + +Spindle is a small CI runner service. Here's a high-level overview of how it operates: + +- Listens for [`sh.tangled.spindle.member`](https://tangled.org/@tangled.org/core/blob/master/lexicons/spindle/member.json) and + [`sh.tangled.repo`](https://tangled.org/@tangled.org/core/blob/master/lexicons/repo.json) records on the Jetstream. +- When a new repo record comes through (typically when you add a spindle to a + repo from the settings), spindle then resolves the underlying knot and + subscribes to repo events (see: + [`sh.tangled.pipeline`](https://tangled.org/@tangled.org/core/blob/master/lexicons/pipeline.json)). +- The spindle engine then handles execution of the pipeline, with results and + logs beamed on the spindle event stream over WebSocket + +## The engines + +Spindle has two execution backends, picked per-workflow with +the [`engine`](#engine) field: + +- **nixery**: executes each step in a fresh Docker container + (Podman works too, if Docker compatibility is enabled so + that `/run/docker.sock` is created), with state persisted + across steps within the `/tangled/workspace` directory. The + base image for the container is constructed on the fly using + [Nixery](https://nixery.dev), which is/rhandy for caching + layers for frequently used packages. +- **microvm**: runs the whole workflow inside its own + microVM, supporting different images, with extra + configuration for NixOS images (e.g. services in workflow file) + See the [engine + README](https://tangled.org/tangled.org/core/blob/master/spindle/engines/microvm/README.md) + for the architecture in depth. + +The pipeline manifest is [specified here](/spindles/workflows/#pipelines). diff --git a/docs/src/content/docs/spindles/index.md b/docs/src/content/docs/spindles/index.md new file mode 100644 index 00000000..4275d1b5 --- /dev/null +++ b/docs/src/content/docs/spindles/index.md @@ -0,0 +1,65 @@ +--- +title: What are spindles? +description: Spindles are Tangled's CI runners, built atop Nix and the AT Protocol. +sidebar: + order: 0 +--- + +Spindle is Tangled's continuous integration runner, built atop Nix +and the AT Protocol. Like knots, spindles are self-hostable by +design: you can use a managed spindle, or run your own and point +your repositories at it. + +A spindle subscribes to repository events. When you push code or +open a pull request, the knot hosting the repository emits an +event; the spindle picks it up, reads the [workflow +manifests](/spindles/workflows/) in `.tangled/workflows/`, spins +up an execution environment, and runs your steps. Status and logs +stream back over websockets, so you can watch runs live from the +appview (or [tail them over SSH](https://blog.tangled.org/ssh)). + +Workflow manifests are intentionally simple. There is no +"marketplace" of workflows or complex job orchestration — +dependencies are just [nixpkgs](https://search.nixos.org) +packages, listed by name, with no toolchain setup. + +## Engines + +Spindles can execute workflows on two engines: + +- **nixery** runs each step in an OCI container (Docker, or + Podman with Docker compatibility). Images are built on the fly + by [Nixery](https://nixery.dev), which turns a list of Nix + packages into a layered container image. +- **microVM** runs the whole workflow inside a QEMU microVM — a + VM with the boring parts removed: no BIOS, no PCI bus to probe, + no emulated graphics. Boot times are fast, isolation is strong + (isolated network namespaces, DNS filtering, blackholed + special-use IP ranges), and workflows can declare NixOS + configuration directly — `services.postgresql.enable: true` gets + you a running database. Existing nixery workflows port over by + changing `engine: nixery` to `engine: microvm`. + +Secrets use AT Protocol [service +auth](https://atproto.com/specs/xrpc#inter-service-authentication-jwt): +the appview makes a signed request using the logged-in user's DID +key, and the spindle verifies the signature against the public key +in their DID document. No shared secrets between services. + +## Where to go next + +- [Workflows](/spindles/workflows/) — the manifest format: + triggers, dependencies, engines, and example recipes +- [Self-hosting a spindle](/spindles/self-hosting/) — run your own + CI on your own machines +- [Secrets with OpenBao](/spindles/secrets/) — back spindle + secrets with OpenBao instead of SQLite +- [Architecture](/spindles/architecture/) — how spindle consumes + records and executes pipelines + +## Further reading + +- [Introducing spindle](https://blog.tangled.org/ci) — the + announcement post +- [Tangled CI runs on microVMs](https://blog.tangled.org/spindle-microvm) + — a deep dive into the QEMU-based microVM engine diff --git a/docs/src/content/docs/spindles/secrets.md b/docs/src/content/docs/spindles/secrets.md new file mode 100644 index 00000000..58cbbbe3 --- /dev/null +++ b/docs/src/content/docs/spindles/secrets.md @@ -0,0 +1,302 @@ +--- +title: Secrets with OpenBao +description: Use OpenBao Proxy for spindle secrets management instead of SQLite. +--- + + +This document covers setting up spindle to use OpenBao for secrets +management via OpenBao Proxy instead of the default SQLite backend. + +## Overview + +Spindle now uses OpenBao Proxy for secrets management. The proxy handles +authentication automatically using AppRole credentials, while spindle +connects to the local proxy instead of directly to the OpenBao server. + +This approach provides better security, automatic token renewal, and +simplified application code. + +## Installation + +Install OpenBao from Nixpkgs: + +```bash +nix shell nixpkgs#openbao # for a local server +``` + +## Setup + +The setup process can is documented for both local development and production. + +### Local development + +Start OpenBao in dev mode: + +```bash +bao server -dev -dev-root-token-id="root" -dev-listen-address=127.0.0.1:8201 +``` + +This starts OpenBao on `http://localhost:8201` with a root token. + +Set up environment for bao CLI: + +```bash +export BAO_ADDR=http://localhost:8200 +export BAO_TOKEN=root +``` + +### Production + +You would typically use a systemd service with a +configuration file. Refer to +[@tangled.org/infra](https://tangled.org/@tangled.org/infra) +for how this can be achieved using Nix. + +Then, initialize the bao server: + +```bash +bao operator init -key-shares=1 -key-threshold=1 +``` + +This will print out an unseal key and a root key. Save them +somewhere (like a password manager). Then unseal the vault +to begin setting it up: + +```bash +bao operator unseal +``` + +All steps below remain the same across both dev and +production setups. + +### Configure openbao server + +Create the spindle KV mount: + +```bash +bao secrets enable -path=spindle -version=2 kv +``` + +Set up AppRole authentication and policy: + +Create a policy file `spindle-policy.hcl`: + +```hcl +# Full access to spindle KV v2 data +path "spindle/data/*" { + capabilities = ["create", "read", "update", "delete"] +} + +# Access to metadata for listing and management +path "spindle/metadata/*" { + capabilities = ["list", "read", "delete", "update"] +} + +# Allow listing at root level +path "spindle/" { + capabilities = ["list"] +} + +# Required for connection testing and health checks +path "auth/token/lookup-self" { + capabilities = ["read"] +} +``` + +Apply the policy and create an AppRole: + +```bash +bao policy write spindle-policy spindle-policy.hcl +bao auth enable approle +bao write auth/approle/role/spindle \ + token_policies="spindle-policy" \ + token_ttl=1h \ + token_max_ttl=4h \ + bind_secret_id=true \ + secret_id_ttl=0 \ + secret_id_num_uses=0 +``` + +Get the credentials: + +```bash +# Get role ID (static) +ROLE_ID=$(bao read -field=role_id auth/approle/role/spindle/role-id) + +# Generate secret ID +SECRET_ID=$(bao write -f -field=secret_id auth/approle/role/spindle/secret-id) + +echo "Role ID: $ROLE_ID" +echo "Secret ID: $SECRET_ID" +``` + +### Create proxy configuration + +Create the credential files: + +```bash +# Create directory for OpenBao files +mkdir -p /tmp/openbao + +# Save credentials +echo "$ROLE_ID" > /tmp/openbao/role-id +echo "$SECRET_ID" > /tmp/openbao/secret-id +chmod 600 /tmp/openbao/role-id /tmp/openbao/secret-id +``` + +Create a proxy configuration file `/tmp/openbao/proxy.hcl`: + +```hcl +# OpenBao server connection +vault { + address = "http://localhost:8200" +} + +# Auto-Auth using AppRole +auto_auth { + method "approle" { + mount_path = "auth/approle" + config = { + role_id_file_path = "/tmp/openbao/role-id" + secret_id_file_path = "/tmp/openbao/secret-id" + } + } + + # Optional: write token to file for debugging + sink "file" { + config = { + path = "/tmp/openbao/token" + mode = 0640 + } + } +} + +# Proxy listener for spindle +listener "tcp" { + address = "127.0.0.1:8201" + tls_disable = true +} + +# Enable API proxy with auto-auth token +api_proxy { + use_auto_auth_token = true +} + +# Enable response caching +cache { + use_auto_auth_token = true +} + +# Logging +log_level = "info" +``` + +### Start the proxy + +Start OpenBao Proxy: + +```bash +bao proxy -config=/tmp/openbao/proxy.hcl +``` + +The proxy will authenticate with OpenBao and start listening on +`127.0.0.1:8201`. + +### Configure spindle + +Set these environment variables for spindle: + +```bash +export SPINDLE_SERVER_SECRETS_PROVIDER=openbao +export SPINDLE_SERVER_SECRETS_OPENBAO_PROXY_ADDR=http://127.0.0.1:8201 +export SPINDLE_SERVER_SECRETS_OPENBAO_MOUNT=spindle +``` + +On startup, spindle will now connect to the local proxy, +which handles all authentication automatically. + +## Production setup for proxy + +For production, you'll want to run the proxy as a service: + +Place your production configuration in +`/etc/openbao/proxy.hcl` with proper TLS settings for the +vault connection. + +## Verifying setup + +Test the proxy directly: + +```bash +# Check proxy health +curl -H "X-Vault-Request: true" http://127.0.0.1:8201/v1/sys/health + +# Test token lookup through proxy +curl -H "X-Vault-Request: true" http://127.0.0.1:8201/v1/auth/token/lookup-self +``` + +Test OpenBao operations through the server: + +```bash +# List all secrets +bao kv list spindle/ + +# Add a test secret via the spindle API, then check it exists +bao kv list spindle/repos/ + +# Get a specific secret +bao kv get spindle/repos/your_repo_path/SECRET_NAME +``` + +## How it works + +- Spindle connects to OpenBao Proxy on localhost (typically + port 8200 or 8201) +- The proxy authenticates with OpenBao using AppRole + credentials +- All spindle requests go through the proxy, which injects + authentication tokens +- Secrets are stored at + `spindle/repos/{sanitized_repo_path}/{secret_key}` +- Repository paths like `did:plc:alice/myrepo` become + `did_plc_alice_myrepo` +- The proxy handles all token renewal automatically +- Spindle no longer manages tokens or authentication + directly + +## Troubleshooting + +**Connection refused**: Check that the OpenBao Proxy is +running and listening on the configured address. + +**403 errors**: Verify the AppRole credentials are correct +and the policy has the necessary permissions. + +**404 route errors**: The spindle KV mount probably doesn't +exist—run the mount creation step again. + +**Proxy authentication failures**: Check the proxy logs and +verify the role-id and secret-id files are readable and +contain valid credentials. + +**Secret not found after writing**: This can indicate policy +permission issues. Verify the policy includes both +`spindle/data/*` and `spindle/metadata/*` paths with +appropriate capabilities. + +Check proxy logs: + +```bash +# If running as systemd service +journalctl -u openbao-proxy -f + +# If running directly, check the console output +``` + +Test AppRole authentication manually: + +```bash +bao write auth/approle/login \ + role_id="$(cat /tmp/openbao/role-id)" \ + secret_id="$(cat /tmp/openbao/secret-id)" +``` diff --git a/docs/src/content/docs/spindles/self-hosting.md b/docs/src/content/docs/spindles/self-hosting.md new file mode 100644 index 00000000..2d489ad5 --- /dev/null +++ b/docs/src/content/docs/spindles/self-hosting.md @@ -0,0 +1,189 @@ +--- +title: Self-hosting a spindle +description: Run your own spindle CI runner, including microVM workflows. +--- + + +## Prerequisites + +- Go +- For the **nixery** engine: Docker (or Podman with Docker + compatibility enabled). +- For the **microVM** engine: a Linux host with KVM, plus the + microVM host dependencies described in [Running microVM + workflows](#running-microvm-workflows). + +## Configuration + +Spindle is configured using environment variables. The following environment variables are available: + +- `SPINDLE_SERVER_LISTEN_ADDR`: The address the server listens on (default: `"0.0.0.0:6555"`). +- `SPINDLE_SERVER_DB_PATH`: The path to the SQLite database file (default: `"spindle.db"`). +- `SPINDLE_SERVER_HOSTNAME`: The hostname of the server (required). +- `SPINDLE_SERVER_JETSTREAM_ENDPOINT`: The endpoint of the Jetstream server (default: `"wss://jetstream1.us-west.bsky.network/subscribe"`). +- `SPINDLE_SERVER_DEV`: A boolean indicating whether the server is running in development mode (default: `false`). +- `SPINDLE_SERVER_OWNER`: The DID of the owner (required). +- `SPINDLE_SERVER_LOG_DIR`: The directory to store workflow logs (default: `"/var/log/spindle"`). +- `SPINDLE_SERVER_DOCKER_SOCKET`: Path to Docker socket to expose to invoked Spindle containers (default: `""`). +- `SPINDLE_PIPELINES_NIXERY`: The Nixery URL (default: `"nixery.tangled.sh"`). +- `SPINDLE_PIPELINES_WORKFLOW_TIMEOUT`: The default workflow timeout (default: `"5m"`). + +For the microVM engine, the following are also available +(prefix `SPINDLE_MICROVM_PIPELINES_`): + +- `SPINDLE_MICROVM_PIPELINES_IMAGE_DIR`: Directory containing + microVM images (**required** to use the engine). See + [Running microVM workflows](#running-microvm-workflows). +- `SPINDLE_MICROVM_PIPELINES_DEFAULT_IMAGE`: Image used when a + workflow doesn't set `image` (default: `"nixos-x86_64"`). +- `SPINDLE_MICROVM_PIPELINES_OVERLAY_DIR`: Where per-workflow + temporary disks are created (default: the system temp dir). +- `SPINDLE_MICROVM_PIPELINES_ENABLE_KVM`: Use KVM hardware + acceleration (default: `true`). Without KVM, guests fall + back to slow software emulation. +- `SPINDLE_MICROVM_PIPELINES_WORKFLOW_TIMEOUT`: Default + workflow timeout (default: `"5m"`). + +Optional resource limits (a value of `0` disables that +limit). The limits cap usage across all running microVM +workflows: + +- `SPINDLE_MICROVM_PIPELINES_MAX_TOTAL_MEMORY_MIB` +- `SPINDLE_MICROVM_PIPELINES_MAX_TOTAL_VCPUS` +- `SPINDLE_MICROVM_PIPELINES_MAX_TOTAL_DISK_MIB` + +Optional cgroup enforcement: + +- `SPINDLE_MICROVM_PIPELINES_ENABLE_CGROUPS`: Place each + workflow's QEMU and slirp4netns in a per-workflow cgroup= + (default: `false`). +- `SPINDLE_MICROVM_PIPELINES_CGROUP_PARENT`: Parent cgroup; + `self` resolves the spindle service's own cgroup (default: + `"self"`). +- `SPINDLE_MICROVM_PIPELINES_CGROUP_PIDS_MAX`: Max processes + per workflow cgroup (default: `4096`). +- `SPINDLE_MICROVM_PIPELINES_CGROUP_SWAP_MAX_MIB`: Max swap + per workflow cgroup (default: `0`, no swap). +- `SPINDLE_MICROVM_PIPELINES_CGROUP_SUPERVISOR_MEMORY_MIN_MIB`: + Memory protected for spindle itself so it isn't OOM-killed + before the workflows (default: `512`). + +To push paths built inside microVMs back to a shared Nix +cache (and read from it), configure the cache (prefix +`SPINDLE_NIX_CACHE_`): + +- `SPINDLE_NIX_CACHE_READ_URLS`: Comma-separated binary cache + URLs the guest reads from. +- `SPINDLE_NIX_CACHE_TRUSTED_PUBLIC_KEYS`: Comma-separated + trusted public keys for those caches. +- `SPINDLE_NIX_CACHE_UPLOAD_URL`: Cache URL that paths built + in the guest are uploaded to. + +## Running spindle + +1. **Set the environment variables.** For example: + + ```shell + export SPINDLE_SERVER_HOSTNAME="your-hostname" + export SPINDLE_SERVER_OWNER="your-did" + ``` + +2. **Build the Spindle binary.** + + ```shell + cd core + go mod download + go build -o cmd/spindle/spindle cmd/spindle/main.go + ``` + +3. **Create the log directory.** + + ```shell + sudo mkdir -p /var/log/spindle + sudo chown $USER:$USER -R /var/log/spindle + ``` + +4. **Run the Spindle binary.** + + ```shell + ./cmd/spindle/spindle + ``` + +Spindle will now start, connect to the Jetstream server, and begin processing pipelines. + +## Running microVM workflows + +The microVM engine needs a few extra things on the host, and +it needs images to boot. + +### Host dependencies + +microVM workflows depend on a handful of host tools and +devices. spindle checks for the ones an image needs right +before it launches, so a missing dependency surfaces as a +clear error. You'll need: + +- `qemu`: the runner. The QEMU binary for the image's arch + must be present (e.g. `qemu-system-x86_64`). +- `mkfs.ext4` (from `e2fsprogs`): to format the per-workflow + writable volumes. +- [`slirp4netns`](https://github.com/rootless-containers/slirp4netns#install), + `ip` (from `iproute2`), `mount` and `unshare` (from `util-linux`): + used to sandbox guest networking. +- `/dev/kvm`: for hardware acceleration (unless you disable + KVM with `SPINDLE_MICROVM_PIPELINES_ENABLE_KVM=false`). +- `/dev/vhost-vsock`: used by QEMU to enable guest-to-host vsock + communication. +- `/dev/vsock`: used by spindle on the host to listen on vsock ports + and accept guest agent connections. +- `/dev/net/tun`: required by `slirp4netns` to set up tap devices + for sandboxed guest networking. + +On NixOS, the [spindle +module](https://tangled.org/tangled.org/core/blob/master/nix/modules/spindle.nix) +puts `qemu`, `e2fsprogs`, `slirp4netns`, `iproute2` and +`util-linux` on the service's `PATH` for you. + +### Container virtualization + +Running `spindle` inside a container (e.g., Docker, Podman, or LXD) requires passing host device nodes into the container and granting the runtime additional privileges. Because spindle uses nested namespaces (`unshare`) and helper tools (`slirp4netns`), the container configuration will require: + +- `/dev/vsock`, `/dev/vhost-vsock`, `/dev/kvm`, and `/dev/net/tun` mapped from the host. +- `NET_ADMIN` and `SYS_ADMIN` capabilities to manage network namespaces and mounts inside the container. +- Relaxed seccomp filters (e.g., `seccomp=unconfined`) and SELinux/AppArmor containment if they restrict namespace creation or device access. + +### Building images + +Images are built with Nix. The flake exposes packages for the +two stock images (use the `-tarball` prefixed ones for a gzipped +tarball you can copy to another host): + +```shell +# a NixOS image +nix build .#spindle-nixos-image +# an Alpine image +nix build .#spindle-alpine-image +``` + +### Installing images + +Spindle looks for images in +`SPINDLE_MICROVM_PIPELINES_IMAGE_DIR`. An image is resolved by +the name a workflow puts in its `image` field, matched +literally against what's on disk: + +1. a directory `/` containing a `spec.json` (next to the + kernel/initrd/store-disk), or +2. a flat `.json` self-contained spec. + +Resolution depends only on the name and what's on disk, never +on the host doing the resolving, so the same workflow resolves +to the same image on every spindle. If you keep multiple +arches side by side, you can name them `-` (e.g. +`nixos-x86_64`, `alpine-aarch64`); the suffix is just part of +the name. To make a name like `nixos` work if you are hosting +multiple arches, you can use symlinks. + +On NixOS, you'll most likely want to use `systemd.tmpfiles.rules` +to set these up declaratively. + diff --git a/docs/src/content/docs/spindles/workflows.md b/docs/src/content/docs/spindles/workflows.md new file mode 100644 index 00000000..b1a49ba6 --- /dev/null +++ b/docs/src/content/docs/spindles/workflows.md @@ -0,0 +1,686 @@ +--- +title: Workflows +description: Write CI/CD pipelines for your repositories using spindle workflows. +--- + + +## Pipelines + +Spindle workflows allow you to write CI/CD pipelines in a +simple format. They're located in the `.tangled/workflows` +directory at the root of your repository, and are defined +using YAML. + +A workflow has a set of common fields that apply no matter +which engine you pick: + +- [Trigger](#trigger): A **required** field that defines + when a workflow should be triggered. +- [Engine](#engine): A **required** field that defines which + engine a workflow should run on. +- [Clone options](#clone-options): An **optional** field + that defines how the repository should be cloned. +- [Environment](#environment): An **optional** field that + allows you to define environment variables. +- [Steps](#steps): An **optional** field that allows you to + define what steps should run in the workflow. + +On top of these, each engine has its own options for things +like dependencies and images. See [Engines](#engines) for +the per-engine fields. + +### Trigger + +The first thing to add to a workflow is the trigger, which +defines when a workflow runs. This is defined using a `when` +field, which takes in a list of conditions. Each condition +has the following fields: + +- `event`: This is a **required** field that defines when + your workflow should run. It's a list that can take one or + more of the following values: + - `push`: The workflow should run every time a commit is + pushed to the repository. + - `pull_request`: The workflow should run every time a + pull request is made or updated. + - `manual`: The workflow can be triggered manually. +- `branch`: Defines which branches the workflow should run + for. If used with the `push` event, commits to the + branch(es) listed here will trigger the workflow. If used + with the `pull_request` event, updates to pull requests + targeting the branch(es) listed here will trigger the + workflow. This field has no effect with the `manual` + event. Supports glob patterns using `*` and `**` (e.g., + `main`, `develop`, `release-*`). Either `branch` or `tag` + (or both) must be specified for `push` events. +- `tag`: Defines which tags the workflow should run for. + Only used with the `push` event - when tags matching the + pattern(s) listed here are pushed, the workflow will + trigger. This field has no effect with `pull_request` or + `manual` events. Supports glob patterns using `*` and `**` + (e.g., `v*`, `v1.*`, `release-**`). Either `branch` or + `tag` (or both) must be specified for `push` events. + +For example, if you'd like to define a workflow that runs +when commits are pushed to the `main` and `develop` +branches, or when pull requests that target the `main` +branch are updated, or manually, you can do so with: + +```yaml +when: + - event: ["push", "manual"] + branch: ["main", "develop"] + - event: ["pull_request"] + branch: ["main"] +``` + +You can also trigger workflows on tag pushes. For instance, +to run a deployment workflow when tags matching `v*` are +pushed: + +```yaml +when: + - event: ["push"] + tag: ["v*"] +``` + +You can even combine branch and tag patterns in a single +constraint (the workflow triggers if either matches): + +```yaml +when: + - event: ["push"] + branch: ["main", "release-*"] + tag: ["v*", "stable"] +``` + +To skip CI for a push, pass a Git push option: + +```sh +git push -o skip-ci +``` + +`ci-skip` is also accepted. + +### Engine + +Next is the engine on which the workflow should run, defined +using the **required** `engine` field. The currently +supported engines are: + +- `nixery`: This uses an instance of + [Nixery](https://nixery.dev) to run steps, which allows + you to add [dependencies](#dependencies) from + Nixpkgs (https://github.com/NixOS/nixpkgs). You can + search for packages on https://search.nixos.org, and + there's a pretty good chance the package(s) you're looking + for will be there. + See [Nixery engine](#nixery-engine). +- `microvm`: Runs the whole workflow inside its own + microVM. Has configuration features for NixOS images + that will let you enable services, do Docker-in-VM, etc. + See [microVM engine](#microvm-engine). + +Example: + +```yaml +engine: "nixery" +``` + +Each engine also adds its own workflow fields (dependencies, +images, services, and so on). These are documented under +[Engines](#engines). + +### Clone options + +When a workflow starts, the first step is to clone the +repository. You can customize this behavior using the +**optional** `clone` field. It has the following fields: + +- `skip`: Setting this to `true` will skip cloning the + repository. This can be useful if your workflow is doing + something that doesn't require anything from the + repository itself. This is `false` by default. +- `depth`: This sets the number of commits, or the "clone + depth", to fetch from the repository. For example, if you + set this to 2, the last 2 commits will be fetched. By + default, the depth is set to 1, meaning only the most + recent commit will be fetched, which is the commit that + triggered the workflow. +- `submodules`: If you use Git submodules + (https://git-scm.com/book/en/v2/Git-Tools-Submodules) + in your repository, setting this field to `true` will + recursively fetch all submodules. This is `false` by + default. + +The default settings are: + +```yaml +clone: + skip: false + depth: 1 + submodules: false +``` + +### Environment + +The `environment` field allows you define environment +variables that will be available throughout the entire +workflow. **Do not put secrets here, these environment +variables are visible to anyone viewing the repository. You +can add secrets for pipelines in your repository's +settings.** + +Example: + +```yaml +environment: + GOOS: "linux" + GOARCH: "arm64" + NODE_ENV: "production" + MY_ENV_VAR: "MY_ENV_VALUE" +``` + +By default, the following environment variables are set: + +- `CI` - Always set to `true` to indicate a CI environment +- `TANGLED_PIPELINE_ID` - The AT URI of the current pipeline +- `TANGLED_PIPELINE_KIND` - One of `push`, `pull_request` or + `manual` +- `TANGLED_REPO_KNOT` - The repository's knot hostname +- `TANGLED_REPO_DID` - The DID of the repository owner +- `TANGLED_REPO_NAME` - The name of the repository +- `TANGLED_REPO_DEFAULT_BRANCH` - The default branch of the + repository +- `TANGLED_REPO_URL` - The full URL to the repository + +These variables are only available when the pipeline is +triggered by a push: + +- `TANGLED_REF` - The full git reference (e.g., + `refs/heads/main` or `refs/tags/v1.0.0`) +- `TANGLED_REF_NAME` - The short name of the reference + (e.g., `main` or `v1.0.0`) +- `TANGLED_REF_TYPE` - The type of reference, either + `branch` or `tag` +- `TANGLED_SHA` - The commit SHA that triggered the pipeline +- `TANGLED_COMMIT_SHA` - Alias for `TANGLED_SHA` + +These variables are only available when the pipeline is +triggered by a pull request: + +- `TANGLED_PR_SOURCE_BRANCH` - The source branch of the pull + request +- `TANGLED_PR_TARGET_BRANCH` - The target branch of the pull + request +- `TANGLED_PR_SOURCE_SHA` - The commit SHA of the source + branch + +### Steps + +The `steps` field allows you to define what steps should run +in the workflow. It's a list of step objects, each with the +following fields: + +- `name`: This field allows you to give your step a name. + This name is visible in your workflow runs, and is used to + describe what the step is doing. +- `command`: This field allows you to define a command to + run in that step. The step is run in a Bash shell, and the + logs from the command will be visible in the pipelines + page on the Tangled website. Any dependencies you added in + your engine's section (see [Engines](#engines)) will be + available to use here. +- `environment`: Similar to the global + [environment](#environment) config, this **optional** + field is a key-value map that allows you to set + environment variables for the step. **Do not put secrets + here, these environment variables are visible to anyone + viewing the repository. You can add secrets for pipelines + in your repository's settings.** + +Example: + +```yaml +steps: + - name: "Build backend" + command: "go build" + environment: + GOOS: "darwin" + GOARCH: "arm64" + - name: "Build frontend" + command: "npm run build" + environment: + NODE_ENV: "production" +``` + +## Engines + +The common fields above apply to every workflow. Each engine +then adds its own fields on top. Pick an engine with the +[`engine`](#engine) field and use the matching section below. + +### Nixery engine + +#### Dependencies + +When you're running a workflow you'll usually need additional +dependencies. The `dependencies` field lets you define which +dependencies to get, and from where. It's a key-value map, +with the key being the registry to fetch dependencies from, +and the value being the list of dependencies to fetch. + +The registry URL syntax can be found [on the nix +manual](https://nix.dev/manual/nix/2.18/command-ref/new-cli/nix3-registry-add). + +Say you want to fetch Node.js and Go from `nixpkgs`, and a +package called `my_pkg` you've made from your own registry +at your repository at +`https://tangled.org/@example.com/my_pkg`. You can define +those dependencies like so: + +```yaml +dependencies: + # nixpkgs + nixpkgs: + - nodejs + - go + # unstable + nixpkgs/nixpkgs-unstable: + - bun + # custom registry + git+https://tangled.org/@example.com/my_pkg: + - my_pkg +``` + +Now these dependencies are available to use in your +workflow! + +#### Complete nixery workflow + +```yaml +# .tangled/workflows/build.yml + +when: + - event: ["push", "manual"] + branch: ["main", "develop"] + - event: ["pull_request"] + branch: ["main"] + +engine: "nixery" + +# using the default values +clone: + skip: false + depth: 1 + submodules: false + +dependencies: + # nixpkgs + nixpkgs: + - nodejs + - go + # custom registry + git+https://tangled.org/@example.com/my_pkg: + - my_pkg + +environment: + GOOS: "linux" + GOARCH: "arm64" + NODE_ENV: "production" + MY_ENV_VAR: "MY_ENV_VALUE" + +steps: + - name: "Build backend" + command: "go build" + environment: + GOOS: "darwin" + GOARCH: "arm64" + - name: "Build frontend" + command: "npm run build" + environment: + NODE_ENV: "production" +``` + +If you want another example of a workflow, you can look at +the one [Tangled uses to build the +project](https://tangled.org/@tangled.org/core/blob/master/.tangled/workflows/build.yml). + +### microVM engine + +#### Image + +A workflow picks the image to boot with the top-level `image` +field: + +```yaml +engine: microvm +image: nixos +``` + +There are two flavours of images: + +- **NixOS images** (e.g. `nixos`): the whole guest is built + with Nix, so you can configure it from the workflow file + itself. The `dependencies`, `services`, `virtualisation`, + `registry` and `caches` fields below are all understood + here, and the guest builds and activates that configuration + before any of your steps run. +- **Non-NixOS images** (e.g. `alpine`): there's no NixOS to + configure, so the workflow-level config fields above have + no effect. You still get a full machine to run steps in. + +The available image names depend on what the spindle operator +has installed. `nixos` and `alpine` are examples. If `image` +is omitted, the spindle's configured default image is used. + +#### Dependencies + +On the microVM engine, `dependencies` is a flat list of +packages that are made available to every step. This field +only applies to **NixOS images**; for other images you can +use the package manager included in a step. + +The guest builds a [`nix develop`](https://nix.dev/manual/nix/2.18/command-ref/new-cli/nix3-develop)-style +devshell from your dependencies and uses it for each step, +so you can, for example, add `pkg-config` and `openssl` and +have the `openssl-sys` crate while compiling a Rust project +just work. + +A bare name like `go` is looked up in nixpkgs. You can also +point at any flake with the `flakeref#attr` syntax, so +`github:nixos/nixpkgs#hello` pulls `hello` straight out of +that flake. + +```yaml +dependencies: + - go + - github:nixos/nixpkgs#hello +``` + +#### Registry + +The `registry` field remaps flake references, the same way +`nix registry` does. This lets you pin or alias the flakes +used by `dependencies`. + +For example, pin `nixpkgs` to `nixos-unstable` so that the +bare `go` above resolves from unstable, and alias your own +flake so you can use `myflake#tool` in `dependencies`: + +```yaml +registry: + nixpkgs: github:nixos/nixpkgs/nixos-unstable + myflake: github:me/x +``` + +#### Caches + +The `caches` field is a map of Nix binary cache URL to its +trusted public key. These are fed into the spindle's read +proxy, so the guest can substitute prebuilt paths from them +instead of building everything from scratch. + +```yaml +caches: + https://nix-community.cachix.org: "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" +``` + +#### Services and virtualisation + +The `services` and `virtualisation` fields are passed straight +through to NixOS. Anything you could write under +`services.*` or `virtualisation.*` in a NixOS configuration, +you can write here, and it's brought up before any of your +steps run. + +As a convenience, `true` works as shorthand for +`.enable = true` anywhere an `enable` option exists (e.g. +`virtualisation.docker: true`). + +```yaml +services: + postgresql: + enable: true + ensureDatabases: ["spindle-workflow"] + ensureUsers: + - name: spindle-workflow + ensureDBOwnership: true + +virtualisation: + docker: true +``` + +#### Recipes + +##### Lint, test and build a Node project + +```yaml +when: + - event: ["push", "pull_request"] + branch: ["main"] + +engine: microvm +image: nixos + +dependencies: + - pnpm + +steps: + - name: "Install dependencies" + command: pnpm install --frozen-lockfile + - name: "Lint and test" + command: | + pnpm run lint + pnpm test + - name: "Build" + command: pnpm run build +``` + +##### Check formatting + +```yaml +when: + - event: ["push", "pull_request"] + branch: ["main"] + +engine: microvm +image: alpine # slimmer image for checking the formatting + +steps: + - name: "Install go" + command: apk add go + - name: "Check formatting" + command: test -z $(gofmt -l .) +``` + +##### Build a Rust project that links OpenSSL + +```yaml +when: + - event: ["push", "pull_request"] + branch: ["main"] + +engine: microvm +image: nixos + +dependencies: + - gcc + - cargo + - rustc + - clippy + - rustfmt + - pkg-config # exports PKG_CONFIG_PATH for the libraries below + - openssl # the C library + headers openssl-sys links against + +steps: + - name: "Check formatting" + command: cargo fmt --check + - name: "Clippy" + command: cargo clippy --all-targets -- -D warnings + - name: "Test" + command: cargo test --all + - name: "Release build" + command: cargo build --release +``` + +##### Run migrations and integration tests against PostgreSQL + +```yaml +when: + - event: ["push", "pull_request"] + branch: ["main"] + +engine: microvm +image: nixos + +environment: + DATABASE_URL: "postgresql:///spindle-workflow?host=/run/postgresql" + +dependencies: + - gcc + - cargo + - rustc + - pkg-config + - openssl + - sqlx-cli + +services: + postgresql: + enable: true + # has to be same name as the user for peer auth to work automatically + ensureDatabases: ["spindle-workflow"] + ensureUsers: + - name: spindle-workflow + ensureDBOwnership: true + +steps: + - name: "Run migrations" + command: sqlx migrate run + - name: "Integration tests" + command: cargo test --all +``` + +##### Build and push a Docker image on tag + +```yaml +when: + - event: ["push"] + tag: ["v*"] + +engine: microvm +image: nixos + +virtualisation: + docker: true + +steps: + - name: "Build and push to ghcr.io" + command: | + set -euo pipefail + + echo "$REGISTRY_TOKEN" | docker login ghcr.io -u "$REGISTRY_USER" --password-stdin + image="ghcr.io/$REGISTRY_USER/myapp:$TANGLED_REF_NAME" + + docker build -t "$image" -t "ghcr.io/$REGISTRY_USER/myapp:latest" . + docker push "$image" + docker push "ghcr.io/$REGISTRY_USER/myapp:latest" +``` + +##### Deploy to Cloudflare Workers on tag + +```yaml +# .tangled/workflows/deploy.yml +when: + - event: ["push"] + tag: ["v*"] + +engine: microvm +image: nixos + +dependencies: + - pnpm + +steps: + - name: "Install dependencies" + command: pnpm install --frozen-lockfile + - name: "Deploy worker" + # `wrangler` picks up `CLOUDFLARE_API_TOKEN` from the env. + # set it under **Settings → Secrets**. + command: pnpm exec wrangler deploy +``` + +##### Publish a release artifact + +```yaml +when: + - event: ["push"] + tag: ["v*"] # trigger on versions + +engine: microvm +image: nixos + +dependencies: + - go + +steps: + - name: "Build release binary" + command: | + mkdir -p dist + CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o dist/myapp ./cmd/myapp + + - name: "Publish artifact record" + command: | + set -euo pipefail + # change this if you're not on `tngl.sh` + PDS="https://tngl.sh" + # also update this to your handle or did + ATP_IDENTIFIER="user.tngl.sh" + ARTIFACT_PATH="dist/myapp" + ARTIFACT_NAME="myapp" + + # set `ATP_APP_PASSWORD` under **Settings → Secrets** + session=$(curl -fsS -X POST "$PDS/xrpc/com.atproto.server.createSession" \ + -H "Content-Type: application/json" \ + -d "{\"identifier\":\"$ATP_IDENTIFIER\",\"password\":\"$ATP_APP_PASSWORD\"}") + jwt=$(echo "$session" | jq -r .accessJwt) + did=$(echo "$session" | jq -r .did) + + # upload the binary as a blob + blob=$(curl -fsS -X POST "$PDS/xrpc/com.atproto.repo.uploadBlob" \ + -H "Authorization: Bearer $jwt" \ + -H "Content-Type: application/octet-stream" \ + --data-binary @"$ARTIFACT_PATH") + + # note that this requires an annotated tag (`git tag -a v1.0.0 -m ...`) + tag_hash=$(git rev-parse "$TANGLED_REF_NAME^{tag}") + tag_bytes=$(printf '%s' "$tag_hash" | xxd -r -p | base64 | tr -d '=') + + # the sh.tangled.repo.artifact record for your artifact + record=$(jq -n \ + --arg did "$did" \ + --arg tag "$tag_bytes" \ + --arg name "$ARTIFACT_NAME" \ + --arg repo "$TANGLED_REPO_URL" \ + --arg created "$(date -Iseconds)" \ + --argjson blob "$(echo "$blob" | jq .blob)" '{ + repo: $did, + collection: "sh.tangled.repo.artifact", + validate: false, + record: { + "$type": "sh.tangled.repo.artifact", + tag: {"$bytes": $tag}, + name: $name, + repo: $repo, + artifact: $blob, + createdAt: $created + } + }') + + # create the record on the PDS + curl -fsS -X POST "$PDS/xrpc/com.atproto.repo.createRecord" \ + -H "Authorization: Bearer $jwt" \ + -H "Content-Type: application/json" \ + -d "$record" +``` + diff --git a/docs/src/content/docs/troubleshooting.md b/docs/src/content/docs/troubleshooting.md new file mode 100644 index 00000000..a44d4685 --- /dev/null +++ b/docs/src/content/docs/troubleshooting.md @@ -0,0 +1,58 @@ +--- +title: Troubleshooting +description: Common issues with login, punchcards, commit verification, and knots. +--- + + +## Login issues + +Owing to the distributed nature of OAuth on AT Protocol, you +may run into issues with logging in. If you run a +self-hosted PDS: + +- You may need to ensure that your PDS is timesynced using + NTP: + - Enable the `ntpd` service + - Run `ntpd -qg` to synchronize your clock +- You may need to increase the default request timeout: + `NODE_OPTIONS="--network-family-autoselection-attempt-timeout=500"` + +## Empty punchcard + +For Tangled to register commits that you make across the +network, you need to setup one of following: + +- The committer email should be a verified email associated + to your account. You can add and verify emails on the + settings page. +- Or, the committer email should be set to your account's + DID: `git config user.email "did:plc:foobar"`. You can find + your account's DID on the settings page + +## Commit is not marked as verified + +Tangled only supports SSH commit signatures. Ensure the SSH public key you use +for signing is uploaded to Tangled. + +To sign commits using an SSH key with git: + +``` +git config --global gpg.format ssh +git config --global user.signingkey ~/.ssh/tangled-key +``` + +To sign commits using an SSH key with jj, add this to your +config: + +``` +[signing] +behavior = "own" +backend = "ssh" +key = "~/.ssh/tangled-key" +``` + +## Self-hosted knot issues + +If you need help troubleshooting a self-hosted knot, check +out the [knot troubleshooting +guide](/knots/self-hosting/#troubleshooting). diff --git a/docs/src/content/docs/why-atproto.md b/docs/src/content/docs/why-atproto.md new file mode 100644 index 00000000..406bbb26 --- /dev/null +++ b/docs/src/content/docs/why-atproto.md @@ -0,0 +1,101 @@ +--- +title: Why AT Protocol? +description: Why Tangled is built on the AT Protocol, and what that buys you over federated or P2P code forges. +--- + +Code collaboration has one of the strongest network effects in +software: your issues, pull requests, reviews, stars, and +followers all live wherever your code lives. On a centralized +forge, all of that belongs to one company. Leaving means starting +over — new identity, new social graph, and years of collaboration +history left behind. + +There are several models for decentralized code collaboration +platforms, ranging from ActivityPub's (Forgejo) federated model, +to Radicle's entirely P2P model. Each solves part of the problem: + +- **Federation** distributes hosting, but your identity is bound + to your instance. If your instance shuts down or defederates, + you lose your account, and cross-instance collaboration is only + as good as the bridges between servers. +- **P2P** gives you full sovereignty, but pushes a lot of + complexity onto every user — key management, discovery, and + availability become your problem. + +Our approach attempts to be the best of both worlds by adopting +the [AT Protocol](https://atproto.com) — a protocol for building +decentralized social applications with a **central identity**. + +## Identity that survives everything + +On the AT Protocol, your identity is a +[DID](https://atproto.com/specs/did) — a stable identifier that is +independent of any server, fronted by a human-readable handle +(usually a domain name you control). Your account lives on a +Personal Data Server (PDS) of your choosing, and you can move +between PDSes without changing who you are. Nothing on Tangled +breaks when you do: your repositories, issues, and followers all +reference your DID, not your server. + +You can think of it as "one account for all of the atmosphere": +the same account you use on Bluesky or any other AT Protocol +application signs you into Tangled. We believe AT Protocol has +greatly simplified one of the hardest parts of social software — +having your friends already on it. + +## Your data, as records you own + +Everything social on Tangled — issues, pull requests, comments, +stars, follows, profile — is stored as records under `sh.tangled.*` +[lexicons](https://atproto.com/guides/lexicon) in *your* AT +Protocol repository, on *your* PDS. Tangled reads them; it doesn't +own them. + +This has practical consequences: + +- Any application can read (and build on) the same records — the + appview at [tangled.org](https://tangled.org) is one view of the + network, [Bobbin](/reference/bobbin/) is another, and anyone can + build their own from the firehose. +- Deleting or migrating your account takes your collaboration + history with you. +- There is no privileged write path: Tangled writes records to + your PDS like any other client would. + +Git data itself is the one exception — repositories are too large +and too specialized to live in a PDS, so they live on +[knots](/knots/), which you can also self-host. Each repository +gets its own DID too, making it stable across renames and +transfers. + +## Services that don't need to trust each other + +Tangled is composed of small, independently hostable services: +knots host git, [spindles](/spindles/) run CI, and the appview +ties the network together. They authenticate to each other with AT +Protocol [service +auth](https://atproto.com/specs/xrpc#inter-service-authentication-jwt): +requests are signed with the user's DID key and verified against +their public DID document. No shared secrets, no API tokens to +provision — any knot can trust a request from any appview, and +vice versa, because identity is cryptographic and global. + +## The tenets + +Tangled's design goals, which the AT Protocol lets us hit all at +once: + +1. **Ownership of data** — your identity and social data are + yours, portable across servers and applications. +2. **Low barrier to entry** — sign up in seconds on + [tangled.org](https://tangled.org/signup), or bring an existing + AT Protocol account. Self-hosting is optional, never required. +3. **No compromise on user-experience** — the appview gives the + whole decentralized network the coherence of a single site. + +## Further reading + +- [Introducing Tangled](https://blog.tangled.org/intro) — the + announcement post +- [What are knots?](/knots/) and [what are spindles?](/spindles/) +- [AT Protocol documentation](https://atproto.com/docs) diff --git a/docs/src/styles/custom.css b/docs/src/styles/custom.css new file mode 100644 index 00000000..c5117130 --- /dev/null +++ b/docs/src/styles/custom.css @@ -0,0 +1,86 @@ +@import "@fontsource-variable/inter"; +@import "@fontsource/ibm-plex-mono/400.css"; +@import "@fontsource/ibm-plex-mono/600.css"; + +/* + * starlight theme mapped onto the appview's palette (tailwind grays, + * slate-100/gray-900 body, monochrome accents). fonts prefer the + * appview's self-hosted files (/static/fonts, present in the nix + * build) and fall back to the fontsource copies in dev. + */ +:root { + --sl-font: "InterVariable", "Inter Variable", system-ui, sans-serif; + --sl-font-mono: "IBMPlexMono", "IBM Plex Mono", monospace; + + /* the appview topbar sits directly on the body background */ + --sl-color-bg-nav: var(--sl-color-black); + --sl-color-bg-sidebar: var(--sl-color-black); + + /* dark — appview dark mode: bg-gray-900, white text */ + --sl-color-white: #ffffff; + --sl-color-gray-1: #f3f4f6; /* gray-100 */ + --sl-color-gray-2: #d1d5db; /* gray-300 */ + --sl-color-gray-3: #9ca3af; /* gray-400 */ + --sl-color-gray-4: #4b5563; /* gray-600 */ + --sl-color-gray-5: #374151; /* gray-700 */ + --sl-color-gray-6: #1f2937; /* gray-800 */ + --sl-color-black: #111827; /* gray-900 */ + + --sl-color-accent-low: #374151; /* gray-700 */ + --sl-color-accent: #6b7280; /* gray-500 */ + --sl-color-accent-high: #d1d5db; /* gray-300 */ +} + +:root[data-theme="light"] { + /* light — white surfaces, black text */ + --sl-color-white: #000000; + --sl-color-gray-1: #1f2937; /* gray-800 */ + --sl-color-gray-2: #374151; /* gray-700 */ + --sl-color-gray-3: #6b7280; /* gray-500 */ + --sl-color-gray-4: #9ca3af; /* gray-400 */ + --sl-color-gray-5: #d1d5db; /* gray-300 */ + --sl-color-gray-6: #f3f4f6; /* gray-100 */ + --sl-color-gray-7: #f9fafb; /* gray-50 */ + --sl-color-black: #ffffff; + + --sl-color-accent-low: #e5e7eb; /* gray-200 */ + --sl-color-accent: #4b5563; /* gray-600 */ + --sl-color-accent-high: #111827; /* gray-900 */ +} + +/* landing page */ +.feature-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); + gap: 1rem; + margin-top: 2rem; +} + +.landing-section { + max-width: 46rem; + margin-top: 2rem; +} + +/* hero tweaks for the landing page */ +[data-has-hero] .hero { + padding-block: 3rem; +} + +/* ascii dolly in the hero (see scripts/gen-ascii-dolly.mjs) */ +.ascii-dolly { + font-family: var(--sl-font-mono); + font-size: 0.55rem; + line-height: 1.2; + color: var(--sl-color-white); + background: none; + border: none; + padding: 0; + margin: 0; + overflow: hidden; + user-select: none; +} + +[data-has-hero] .hero h1 { + font-size: clamp(2rem, 5vw, 3.25rem); + letter-spacing: -0.02em; +} diff --git a/docs/src/styles/sidebar-icons.css b/docs/src/styles/sidebar-icons.css new file mode 100644 index 00000000..395f3bc5 --- /dev/null +++ b/docs/src/styles/sidebar-icons.css @@ -0,0 +1,88 @@ +/* GENERATED by scripts/gen-sidebar-icons.mjs — do not edit by hand */ + +#starlight__sidebar a[data-icon]::before { + content: ""; + display: inline-block; + width: 1rem; + height: 1rem; + margin-inline-end: 0.4em; + vertical-align: -0.19em; + background-color: currentColor; + -webkit-mask-size: 100%; + mask-size: 100%; +} + +#starlight__sidebar a[data-icon="rocket"]::before { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M4.5%2016.5c-1.5%201.26-2%205-2%205s3.74-.5%205-2c.71-.84.7-2.13-.09-2.91a2.18%202.18%200%200%200-2.91-.09z%22%20%2F%3E%3Cpath%20d%3D%22m12%2015-3-3a22%2022%200%200%201%202-3.95A12.88%2012.88%200%200%201%2022%202c0%202.72-.78%207.5-6%2011a22.35%2022.35%200%200%201-4%202z%22%20%2F%3E%3Cpath%20d%3D%22M9%2012H4s.55-3.03%202-4c1.62-1.08%205%200%205%200%22%20%2F%3E%3Cpath%20d%3D%22M12%2015v5s3.03-.55%204-2c1.08-1.62%200-5%200-5%22%20%2F%3E%3C%2Fsvg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M4.5%2016.5c-1.5%201.26-2%205-2%205s3.74-.5%205-2c.71-.84.7-2.13-.09-2.91a2.18%202.18%200%200%200-2.91-.09z%22%20%2F%3E%3Cpath%20d%3D%22m12%2015-3-3a22%2022%200%200%201%202-3.95A12.88%2012.88%200%200%201%2022%202c0%202.72-.78%207.5-6%2011a22.35%2022.35%200%200%201-4%202z%22%20%2F%3E%3Cpath%20d%3D%22M9%2012H4s.55-3.03%202-4c1.62-1.08%205%200%205%200%22%20%2F%3E%3Cpath%20d%3D%22M12%2015v5s3.03-.55%204-2c1.08-1.62%200-5%200-5%22%20%2F%3E%3C%2Fsvg%3E"); +} + +#starlight__sidebar a[data-icon="at-sign"]::before { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%224%22%20%2F%3E%3Cpath%20d%3D%22M16%208v5a3%203%200%200%200%206%200v-1a10%2010%200%201%200-4%208%22%20%2F%3E%3C%2Fsvg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%224%22%20%2F%3E%3Cpath%20d%3D%22M16%208v5a3%203%200%200%200%206%200v-1a10%2010%200%201%200-4%208%22%20%2F%3E%3C%2Fsvg%3E"); +} + +#starlight__sidebar a[data-icon="globe"]::before { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%2210%22%20%2F%3E%3Cpath%20d%3D%22M12%202a14.5%2014.5%200%200%200%200%2020%2014.5%2014.5%200%200%200%200-20%22%20%2F%3E%3Cpath%20d%3D%22M2%2012h20%22%20%2F%3E%3C%2Fsvg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%2210%22%20%2F%3E%3Cpath%20d%3D%22M12%202a14.5%2014.5%200%200%200%200%2020%2014.5%2014.5%200%200%200%200-20%22%20%2F%3E%3Cpath%20d%3D%22M2%2012h20%22%20%2F%3E%3C%2Fsvg%3E"); +} + +#starlight__sidebar a[data-icon="life-buoy"]::before { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%2210%22%20%2F%3E%3Cpath%20d%3D%22m4.93%204.93%204.24%204.24%22%20%2F%3E%3Cpath%20d%3D%22m14.83%209.17%204.24-4.24%22%20%2F%3E%3Cpath%20d%3D%22m14.83%2014.83%204.24%204.24%22%20%2F%3E%3Cpath%20d%3D%22m9.17%2014.83-4.24%204.24%22%20%2F%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%224%22%20%2F%3E%3C%2Fsvg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%2210%22%20%2F%3E%3Cpath%20d%3D%22m4.93%204.93%204.24%204.24%22%20%2F%3E%3Cpath%20d%3D%22m14.83%209.17%204.24-4.24%22%20%2F%3E%3Cpath%20d%3D%22m14.83%2014.83%204.24%204.24%22%20%2F%3E%3Cpath%20d%3D%22m9.17%2014.83-4.24%204.24%22%20%2F%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%224%22%20%2F%3E%3C%2Fsvg%3E"); +} + +#starlight__sidebar a[data-icon="server"]::before { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Crect%20width%3D%2220%22%20height%3D%228%22%20x%3D%222%22%20y%3D%222%22%20rx%3D%222%22%20ry%3D%222%22%20%2F%3E%3Crect%20width%3D%2220%22%20height%3D%228%22%20x%3D%222%22%20y%3D%2214%22%20rx%3D%222%22%20ry%3D%222%22%20%2F%3E%3Cline%20x1%3D%226%22%20x2%3D%226.01%22%20y1%3D%226%22%20y2%3D%226%22%20%2F%3E%3Cline%20x1%3D%226%22%20x2%3D%226.01%22%20y1%3D%2218%22%20y2%3D%2218%22%20%2F%3E%3C%2Fsvg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Crect%20width%3D%2220%22%20height%3D%228%22%20x%3D%222%22%20y%3D%222%22%20rx%3D%222%22%20ry%3D%222%22%20%2F%3E%3Crect%20width%3D%2220%22%20height%3D%228%22%20x%3D%222%22%20y%3D%2214%22%20rx%3D%222%22%20ry%3D%222%22%20%2F%3E%3Cline%20x1%3D%226%22%20x2%3D%226.01%22%20y1%3D%226%22%20y2%3D%226%22%20%2F%3E%3Cline%20x1%3D%226%22%20x2%3D%226.01%22%20y1%3D%2218%22%20y2%3D%2218%22%20%2F%3E%3C%2Fsvg%3E"); +} + +#starlight__sidebar a[data-icon="hard-drive"]::before { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cline%20x1%3D%2222%22%20x2%3D%222%22%20y1%3D%2212%22%20y2%3D%2212%22%20%2F%3E%3Cpath%20d%3D%22M5.45%205.11%202%2012v6a2%202%200%200%200%202%202h16a2%202%200%200%200%202-2v-6l-3.45-6.89A2%202%200%200%200%2016.76%204H7.24a2%202%200%200%200-1.79%201.11z%22%20%2F%3E%3Cline%20x1%3D%226%22%20x2%3D%226.01%22%20y1%3D%2216%22%20y2%3D%2216%22%20%2F%3E%3Cline%20x1%3D%2210%22%20x2%3D%2210.01%22%20y1%3D%2216%22%20y2%3D%2216%22%20%2F%3E%3C%2Fsvg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cline%20x1%3D%2222%22%20x2%3D%222%22%20y1%3D%2212%22%20y2%3D%2212%22%20%2F%3E%3Cpath%20d%3D%22M5.45%205.11%202%2012v6a2%202%200%200%200%202%202h16a2%202%200%200%200%202-2v-6l-3.45-6.89A2%202%200%200%200%2016.76%204H7.24a2%202%200%200%200-1.79%201.11z%22%20%2F%3E%3Cline%20x1%3D%226%22%20x2%3D%226.01%22%20y1%3D%2216%22%20y2%3D%2216%22%20%2F%3E%3Cline%20x1%3D%2210%22%20x2%3D%2210.01%22%20y1%3D%2216%22%20y2%3D%2216%22%20%2F%3E%3C%2Fsvg%3E"); +} + +#starlight__sidebar a[data-icon="workflow"]::before { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Crect%20width%3D%228%22%20height%3D%228%22%20x%3D%223%22%20y%3D%223%22%20rx%3D%222%22%20%2F%3E%3Cpath%20d%3D%22M7%2011v4a2%202%200%200%200%202%202h4%22%20%2F%3E%3Crect%20width%3D%228%22%20height%3D%228%22%20x%3D%2213%22%20y%3D%2213%22%20rx%3D%222%22%20%2F%3E%3C%2Fsvg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Crect%20width%3D%228%22%20height%3D%228%22%20x%3D%223%22%20y%3D%223%22%20rx%3D%222%22%20%2F%3E%3Cpath%20d%3D%22M7%2011v4a2%202%200%200%200%202%202h4%22%20%2F%3E%3Crect%20width%3D%228%22%20height%3D%228%22%20x%3D%2213%22%20y%3D%2213%22%20rx%3D%222%22%20%2F%3E%3C%2Fsvg%3E"); +} + +#starlight__sidebar a[data-icon="play"]::before { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M5%205a2%202%200%200%201%203.008-1.728l11.997%206.998a2%202%200%200%201%20.003%203.458l-12%207A2%202%200%200%201%205%2019z%22%20%2F%3E%3C%2Fsvg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M5%205a2%202%200%200%201%203.008-1.728l11.997%206.998a2%202%200%200%201%20.003%203.458l-12%207A2%202%200%200%201%205%2019z%22%20%2F%3E%3C%2Fsvg%3E"); +} + +#starlight__sidebar a[data-icon="key-round"]::before { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M2.586%2017.414A2%202%200%200%200%202%2018.828V21a1%201%200%200%200%201%201h3a1%201%200%200%200%201-1v-1a1%201%200%200%201%201-1h1a1%201%200%200%200%201-1v-1a1%201%200%200%201%201-1h.172a2%202%200%200%200%201.414-.586l.814-.814a6.5%206.5%200%201%200-4-4z%22%20%2F%3E%3Ccircle%20cx%3D%2216.5%22%20cy%3D%227.5%22%20r%3D%22.5%22%20fill%3D%22currentColor%22%20%2F%3E%3C%2Fsvg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M2.586%2017.414A2%202%200%200%200%202%2018.828V21a1%201%200%200%200%201%201h3a1%201%200%200%200%201-1v-1a1%201%200%200%201%201-1h1a1%201%200%200%200%201-1v-1a1%201%200%200%201%201-1h.172a2%202%200%200%200%201.414-.586l.814-.814a6.5%206.5%200%201%200-4-4z%22%20%2F%3E%3Ccircle%20cx%3D%2216.5%22%20cy%3D%227.5%22%20r%3D%22.5%22%20fill%3D%22currentColor%22%20%2F%3E%3C%2Fsvg%3E"); +} + +#starlight__sidebar a[data-icon="network"]::before { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Crect%20x%3D%2216%22%20y%3D%2216%22%20width%3D%226%22%20height%3D%226%22%20rx%3D%221%22%20%2F%3E%3Crect%20x%3D%222%22%20y%3D%2216%22%20width%3D%226%22%20height%3D%226%22%20rx%3D%221%22%20%2F%3E%3Crect%20x%3D%229%22%20y%3D%222%22%20width%3D%226%22%20height%3D%226%22%20rx%3D%221%22%20%2F%3E%3Cpath%20d%3D%22M5%2016v-3a1%201%200%200%201%201-1h12a1%201%200%200%201%201%201v3%22%20%2F%3E%3Cpath%20d%3D%22M12%2012V8%22%20%2F%3E%3C%2Fsvg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Crect%20x%3D%2216%22%20y%3D%2216%22%20width%3D%226%22%20height%3D%226%22%20rx%3D%221%22%20%2F%3E%3Crect%20x%3D%222%22%20y%3D%2216%22%20width%3D%226%22%20height%3D%226%22%20rx%3D%221%22%20%2F%3E%3Crect%20x%3D%229%22%20y%3D%222%22%20width%3D%226%22%20height%3D%226%22%20rx%3D%221%22%20%2F%3E%3Cpath%20d%3D%22M5%2016v-3a1%201%200%200%201%201-1h12a1%201%200%200%201%201%201v3%22%20%2F%3E%3Cpath%20d%3D%22M12%2012V8%22%20%2F%3E%3C%2Fsvg%3E"); +} + +#starlight__sidebar a[data-icon="webhook"]::before { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M18%2016.98h-5.99c-1.1%200-1.95.94-2.48%201.9A4%204%200%200%201%202%2017c.01-.7.2-1.4.57-2%22%20%2F%3E%3Cpath%20d%3D%22m6%2017%203.13-5.78c.53-.97.1-2.18-.5-3.1a4%204%200%201%201%206.89-4.06%22%20%2F%3E%3Cpath%20d%3D%22m12%206%203.13%205.73C15.66%2012.7%2016.9%2013%2018%2013a4%204%200%200%201%200%208%22%20%2F%3E%3C%2Fsvg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M18%2016.98h-5.99c-1.1%200-1.95.94-2.48%201.9A4%204%200%200%201%202%2017c.01-.7.2-1.4.57-2%22%20%2F%3E%3Cpath%20d%3D%22m6%2017%203.13-5.78c.53-.97.1-2.18-.5-3.1a4%204%200%201%201%206.89-4.06%22%20%2F%3E%3Cpath%20d%3D%22m12%206%203.13%205.73C15.66%2012.7%2016.9%2013%2018%2013a4%204%200%200%201%200%208%22%20%2F%3E%3C%2Fsvg%3E"); +} + +#starlight__sidebar a[data-icon="cable"]::before { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M17%2019a1%201%200%200%201-1-1v-2a2%202%200%200%201%202-2h2a2%202%200%200%201%202%202v2a1%201%200%200%201-1%201z%22%20%2F%3E%3Cpath%20d%3D%22M17%2021v-2%22%20%2F%3E%3Cpath%20d%3D%22M19%2014V6.5a1%201%200%200%200-7%200v11a1%201%200%200%201-7%200V10%22%20%2F%3E%3Cpath%20d%3D%22M21%2021v-2%22%20%2F%3E%3Cpath%20d%3D%22M3%205V3%22%20%2F%3E%3Cpath%20d%3D%22M4%2010a2%202%200%200%201-2-2V6a1%201%200%200%201%201-1h4a1%201%200%200%201%201%201v2a2%202%200%200%201-2%202z%22%20%2F%3E%3Cpath%20d%3D%22M7%205V3%22%20%2F%3E%3C%2Fsvg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M17%2019a1%201%200%200%201-1-1v-2a2%202%200%200%201%202-2h2a2%202%200%200%201%202%202v2a1%201%200%200%201-1%201z%22%20%2F%3E%3Cpath%20d%3D%22M17%2021v-2%22%20%2F%3E%3Cpath%20d%3D%22M19%2014V6.5a1%201%200%200%200-7%200v11a1%201%200%200%201-7%200V10%22%20%2F%3E%3Cpath%20d%3D%22M21%2021v-2%22%20%2F%3E%3Cpath%20d%3D%22M3%205V3%22%20%2F%3E%3Cpath%20d%3D%22M4%2010a2%202%200%200%201-2-2V6a1%201%200%200%201%201-1h4a1%201%200%200%201%201%201v2a2%202%200%200%201-2%202z%22%20%2F%3E%3Cpath%20d%3D%22M7%205V3%22%20%2F%3E%3C%2Fsvg%3E"); +} + +#starlight__sidebar a[data-icon="arrow-up-circle"]::before { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%2210%22%20%2F%3E%3Cpath%20d%3D%22m16%2012-4-4-4%204%22%20%2F%3E%3Cpath%20d%3D%22M12%2016V8%22%20%2F%3E%3C%2Fsvg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%2210%22%20%2F%3E%3Cpath%20d%3D%22m16%2012-4-4-4%204%22%20%2F%3E%3Cpath%20d%3D%22M12%2016V8%22%20%2F%3E%3C%2Fsvg%3E"); +} + +#starlight__sidebar a[data-icon="hammer"]::before { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22m15%2012-9.373%209.373a1%201%200%200%201-3.001-3L12%209%22%20%2F%3E%3Cpath%20d%3D%22m18%2015%204-4%22%20%2F%3E%3Cpath%20d%3D%22m21.5%2011.5-1.914-1.914A2%202%200%200%201%2019%208.172v-.344a2%202%200%200%200-.586-1.414l-1.657-1.657A6%206%200%200%200%2012.516%203H9l1.243%201.243A6%206%200%200%201%2012%208.485V10l2%202h1.172a2%202%200%200%201%201.414.586L18.5%2014.5%22%20%2F%3E%3C%2Fsvg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22m15%2012-9.373%209.373a1%201%200%200%201-3.001-3L12%209%22%20%2F%3E%3Cpath%20d%3D%22m18%2015%204-4%22%20%2F%3E%3Cpath%20d%3D%22m21.5%2011.5-1.914-1.914A2%202%200%200%201%2019%208.172v-.344a2%202%200%200%200-.586-1.414l-1.657-1.657A6%206%200%200%200%2012.516%203H9l1.243%201.243A6%206%200%200%201%2012%208.485V10l2%202h1.172a2%202%200%200%201%201.414.586L18.5%2014.5%22%20%2F%3E%3C%2Fsvg%3E"); +} + +#starlight__sidebar a[data-icon="git-pull-request"]::before { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2218%22%20cy%3D%2218%22%20r%3D%223%22%20%2F%3E%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20%2F%3E%3Cpath%20d%3D%22M13%206h3a2%202%200%200%201%202%202v7%22%20%2F%3E%3Cline%20x1%3D%226%22%20x2%3D%226%22%20y1%3D%229%22%20y2%3D%2221%22%20%2F%3E%3C%2Fsvg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2218%22%20cy%3D%2218%22%20r%3D%223%22%20%2F%3E%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20%2F%3E%3Cpath%20d%3D%22M13%206h3a2%202%200%200%201%202%202v7%22%20%2F%3E%3Cline%20x1%3D%226%22%20x2%3D%226%22%20y1%3D%229%22%20y2%3D%2221%22%20%2F%3E%3C%2Fsvg%3E"); +} diff --git a/docs/src/styles/tangled.css b/docs/src/styles/tangled.css new file mode 100644 index 00000000..edb2ad2d --- /dev/null +++ b/docs/src/styles/tangled.css @@ -0,0 +1,97 @@ +/* + * the appview's stylesheet, compiled against the docs tailwind + * config (see tailwind.config.cjs). this brings in the base layer + * (14px root font size, link/code styles, selection color) and the + * component classes (.btn, .btn-create, .btn-flat, ...). + */ +@import "../../../input.css"; + +/* preflight is disabled (starlight has its own reset), but tailwind's + * border utilities assume preflight's `border-style: solid` reset */ +.btn, +.hero .sl-link-button, +[class*="border-"] { + border-style: solid; +} + +/* logo at the same size as the appview topbar logotype (h-8) */ +.site-title img { + @apply h-8; +} + +/* markdown content, styled like the appview renders readmes: + * plain black/white links, bordered h1/h2, gray inline code */ +/* like `prose` links in appview readmes: underlined, medium weight. + * NB: the card exclusion must not say `.btn` here — tailwind's @apply + * of .btn/.btn-create elsewhere clones every rule that mentions .btn + * in its selector, re-introducing the underline through the clone */ +.sl-markdown-content a:not(.feature-card):not(:where(.not-content *)) { + @apply text-black underline font-medium hover:text-gray-800 dark:text-white dark:hover:text-gray-300; +} + +.sl-markdown-content h1:not(:where(.not-content *)) { + @apply mt-3 pb-3 border-b border-gray-300 dark:border-gray-600; +} + +.sl-markdown-content h2:not(:where(.not-content *)) { + @apply mt-3 pb-3 border-b border-gray-200 dark:border-gray-700; +} + +.sl-markdown-content code:not(:where(.not-content *)) { + @apply font-mono rounded px-1 bg-gray-100 text-black dark:bg-gray-700 dark:text-white; +} + +/* the appview's base `label` style (block, py-2, text-sm) clobbers + * starlight's select labels — starlight's own styles live in css + * cascade layers and lose to the unlayered appview css */ +starlight-theme-select label, +starlight-lang-select label { + display: flex; + padding: 0; + font-size: unset; + color: var(--sl-color-gray-1); +} + +starlight-theme-select label:hover, +starlight-lang-select label:hover { + color: var(--sl-color-gray-2); +} + +/* sidebar current-page pill, like the appview settings sidebar tabs */ +.sidebar-content a[aria-current="page"], +.sidebar-content a[aria-current="page"]:hover, +.sidebar-content a[aria-current="page"]:focus { + @apply bg-gray-100 text-black font-semibold dark:bg-gray-700 dark:text-white; +} + +/* text links in the top bar (see components/HeaderLinks.astro) */ +a.header-link, +a.header-link:hover { + @apply text-sm no-underline text-gray-500 hover:text-black dark:text-gray-400 dark:hover:text-white; +} + +/* compact next/prev pagination links */ +.pagination-links a { + @apply p-3 gap-2 rounded no-underline hover:no-underline; +} + +.pagination-links a .link-title { + @apply text-base font-semibold; +} + +.pagination-links a svg { + @apply size-5; +} + +/* restyle starlight's hero buttons with the appview button classes */ +.hero .sl-link-button.primary { + @apply btn-create text-base; +} + +.hero .sl-link-button.primary:hover { + @apply text-white no-underline; +} + +.hero .sl-link-button.minimal { + @apply btn-flat text-base; +} diff --git a/docs/styles.css b/docs/styles.css deleted file mode 100644 index 32f59069..00000000 --- a/docs/styles.css +++ /dev/null @@ -1,216 +0,0 @@ -:root { - --pagefind-ui-scale: 0.875 !important; - --pagefind-ui-border-width: 1px !important; - --pagefind-ui-border-radius: 4px !important; - --pagefind-ui-border: #d1d5db !important; - --pagefind-ui-background: #f9fafb !important; - --pagefind-ui-text: #111827 !important; - --pagefind-ui-primary: #6b7280 !important; - --pagefind-ui-font: inherit !important; -} - -.pagefind-ui__search-input { - font-weight: normal !important; -} - -@media (prefers-color-scheme: dark) { - :root { - --pagefind-ui-border: #4b5563 !important; - --pagefind-ui-background: #1f2937 !important; - --pagefind-ui-text: #f9fafb !important; - --pagefind-ui-primary: #9ca3af !important; - } -} - -@media (max-width: 768px) { - .pagefind-ui__result-thumb { - width: 0 !important; - } - .pagefind-ui__result-excerpt, - .pagefind-ui__result-title { - overflow-wrap: break-word; - word-break: break-word; - } - .pagefind-ui__search-input, - .pagefind-ui__search-clear { - max-width: 100%; - } - .pagefind-ui__form { - overflow: hidden; - } -} - -svg { - width: 16px; - height: 16px; -} - -:root { - --syntax-alert: #d20f39; - --syntax-annotation: #fe640b; - --syntax-attribute: #df8e1d; - --syntax-basen: #40a02b; - --syntax-builtin: #1e66f5; - --syntax-controlflow: #8839ef; - --syntax-char: #04a5e5; - --syntax-constant: #fe640b; - --syntax-comment: #9ca0b0; - --syntax-commentvar: #7c7f93; - --syntax-documentation: #9ca0b0; - --syntax-datatype: #df8e1d; - --syntax-decval: #40a02b; - --syntax-error: #d20f39; - --syntax-extension: #4c4f69; - --syntax-float: #40a02b; - --syntax-function: #1e66f5; - --syntax-import: #40a02b; - --syntax-information: #04a5e5; - --syntax-keyword: #8839ef; - --syntax-operator: #179299; - --syntax-other: #8839ef; - --syntax-preprocessor: #ea76cb; - --syntax-specialchar: #04a5e5; - --syntax-specialstring: #ea76cb; - --syntax-string: #40a02b; - --syntax-variable: #8839ef; - --syntax-verbatimstring: #40a02b; - --syntax-warning: #df8e1d; -} - -@media (prefers-color-scheme: dark) { - :root { - --syntax-alert: #f38ba8; - --syntax-annotation: #fab387; - --syntax-attribute: #f9e2af; - --syntax-basen: #a6e3a1; - --syntax-builtin: #89b4fa; - --syntax-controlflow: #cba6f7; - --syntax-char: #89dceb; - --syntax-constant: #fab387; - --syntax-comment: #6c7086; - --syntax-commentvar: #585b70; - --syntax-documentation: #6c7086; - --syntax-datatype: #f9e2af; - --syntax-decval: #a6e3a1; - --syntax-error: #f38ba8; - --syntax-extension: #cdd6f4; - --syntax-float: #a6e3a1; - --syntax-function: #89b4fa; - --syntax-import: #a6e3a1; - --syntax-information: #89dceb; - --syntax-keyword: #cba6f7; - --syntax-operator: #94e2d5; - --syntax-other: #cba6f7; - --syntax-preprocessor: #f5c2e7; - --syntax-specialchar: #89dceb; - --syntax-specialstring: #f5c2e7; - --syntax-string: #a6e3a1; - --syntax-variable: #cba6f7; - --syntax-verbatimstring: #a6e3a1; - --syntax-warning: #f9e2af; - } -} - -/* pandoc syntax highlighting classes */ -code span.al { - color: var(--syntax-alert); - font-weight: bold; -} /* alert */ -code span.an { - color: var(--syntax-annotation); - font-weight: bold; - font-style: italic; -} /* annotation */ -code span.at { - color: var(--syntax-attribute); -} /* attribute */ -code span.bn { - color: var(--syntax-basen); -} /* basen */ -code span.bu { - color: var(--syntax-builtin); -} /* builtin */ -code span.cf { - color: var(--syntax-controlflow); - font-weight: bold; -} /* controlflow */ -code span.ch { - color: var(--syntax-char); -} /* char */ -code span.cn { - color: var(--syntax-constant); -} /* constant */ -code span.co { - color: var(--syntax-comment); - font-style: italic; -} /* comment */ -code span.cv { - color: var(--syntax-commentvar); - font-weight: bold; - font-style: italic; -} /* commentvar */ -code span.do { - color: var(--syntax-documentation); - font-style: italic; -} /* documentation */ -code span.dt { - color: var(--syntax-datatype); -} /* datatype */ -code span.dv { - color: var(--syntax-decval); -} /* decval */ -code span.er { - color: var(--syntax-error); - font-weight: bold; -} /* error */ -code span.ex { - color: var(--syntax-extension); -} /* extension */ -code span.fl { - color: var(--syntax-float); -} /* float */ -code span.fu { - color: var(--syntax-function); -} /* function */ -code span.im { - color: var(--syntax-import); - font-weight: bold; -} /* import */ -code span.in { - color: var(--syntax-information); - font-weight: bold; - font-style: italic; -} /* information */ -code span.kw { - color: var(--syntax-keyword); - font-weight: bold; -} /* keyword */ -code span.op { - color: var(--syntax-operator); -} /* operator */ -code span.ot { - color: var(--syntax-other); -} /* other */ -code span.pp { - color: var(--syntax-preprocessor); -} /* preprocessor */ -code span.sc { - color: var(--syntax-specialchar); -} /* specialchar */ -code span.ss { - color: var(--syntax-specialstring); -} /* specialstring */ -code span.st { - color: var(--syntax-string); -} /* string */ -code span.va { - color: var(--syntax-variable); -} /* variable */ -code span.vs { - color: var(--syntax-verbatimstring); -} /* verbatimstring */ -code span.wa { - color: var(--syntax-warning); - font-weight: bold; - font-style: italic; -} /* warning */ diff --git a/docs/tailwind.config.cjs b/docs/tailwind.config.cjs new file mode 100644 index 00000000..925124a1 --- /dev/null +++ b/docs/tailwind.config.cjs @@ -0,0 +1,35 @@ +// compiles the appview's input.css for the docs site. we can't require +// the root tailwind.config.js here (it resolves its plugins from the +// standalone tailwind CLI in the nix build), so the font stacks are +// duplicated from it. differences from the appview config: +// - scans the docs sources instead of appview templates +// - keys dark mode off starlight's data-theme attribute instead of +// prefers-color-scheme, so the theme toggle works +// - disables preflight: starlight ships its own reset + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ["./src/**/*.{astro,html,md,mdx}"], + darkMode: ["class", '[data-theme="dark"]'], + corePlugins: { + preflight: false, + }, + theme: { + extend: { + fontFamily: { + sans: ["InterVariable", "system-ui", "sans-serif", "ui-sans-serif"], + mono: [ + "IBMPlexMono", + "ui-monospace", + "SFMono-Regular", + "Menlo", + "Monaco", + "Consolas", + "Liberation Mono", + "Courier New", + "monospace", + ], + }, + }, + }, +}; diff --git a/docs/template.html b/docs/template.html deleted file mode 100644 index 46a0e7b6..00000000 --- a/docs/template.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - - $for(author-meta)$ - - $endfor$ - - $if(date-meta)$ - - $endif$ - - $if(keywords)$ - - $endif$ - - $if(description-meta)$ - - $endif$ - - $pagetitle$ - - - - $for(css)$ - - $endfor$ - - $for(header-includes)$ - $header-includes$ - $endfor$ - - - - - - - - - - - - $for(include-before)$ - $include-before$ - $endfor$ - - $if(toc)$ - -
- -
- -
-
-
- - ${ logo.html() } - ${ search.html() } - ${ table-of-contents:toc.html() } -
- ${ single-page:mode.html() } -
-
- - - - $endif$ - -
-
- $if(top)$ - $-- only print title block if this is NOT the top page - $else$ - $if(title)$ -
-

$title$

- $if(subtitle)$ -

$subtitle$

- $endif$ - $for(author)$ -

$author$

- $endfor$ - $if(date)$ -

Updated on $date$

- $endif$ - $endif$ -
- $if(abstract)$ -
- $abstract$ -
- $endif$ - $endif$ - -
- $body$ -
-
- -
- $for(include-after)$ - $include-after$ - $endfor$ - - diff --git a/docs/toc.html b/docs/toc.html deleted file mode 100644 index 86edc981..00000000 --- a/docs/toc.html +++ /dev/null @@ -1,4 +0,0 @@ -
- $table-of-contents$ -
- diff --git a/docs/tsconfig.json b/docs/tsconfig.json new file mode 100644 index 00000000..8bf91d3b --- /dev/null +++ b/docs/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "astro/tsconfigs/strict", + "include": [".astro/types.d.ts", "**/*"], + "exclude": ["dist"] +} -- 2.51.2