diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml new file mode 100644 index 0000000..72cf340 --- /dev/null +++ b/.github/workflows/docs-deploy.yml @@ -0,0 +1,75 @@ +name: Deploy Docs + +on: + # build for docs changes on main + push: + # TODO: reenable when ready to merge + # branches: + # - main + paths: + - docs/** + + # or manual triggers from web ui + workflow_dispatch: + +# allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: pages + cancel-in-progress: false + +jobs: + # Build job + docs-build: + name: Docs Build + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install tools + uses: jdx/mise-action@v2 + with: + working_directory: docs + + # - uses: pnpm/action-setup@v3 + # - name: Setup Node + # uses: actions/setup-node@v4 + # with: + # node-version: lts + # cache: pnpm + + - name: Install dependencies + working-directory: docs + run: pnpm install + + - name: Build with VitePress + working-directory: docs + run: pnpm run docs:build + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs/.vitepress/dist + + # Deployment job + docs-deploy: + name: Docs Deploy + runs-on: ubuntu-latest + needs: docs-build + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Setup Pages + uses: actions/configure-pages@v4 + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/README.md b/README.md index 769b907..ef6b38a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,51 @@ -# beavercds-ng -new implementation of beavercds/rcds +# beavercds-backend + +A modern, cloud-native framework for managing CTF challenge deployment. + +## What is this? + + +## Getting Started + +Install like any other Rust binary: +``` +cargo install --git https://github.com/osusec/beavercds-backend +``` + +See the documentation and guides for [challenge authors](https://beavercds.info) and [infrastructure admins](https://beavercds.info). + +## Contributing + +Contributions are welcome! Check out some of the TODO work on the issue tracker +for inspiration. + +We use the standard `cargo build` process for building `beavercds` itself, and +there is a test repository under `tests/repo/` to use during development. + +Dependencies: +- `rust` @ latest (currently 1.88) +- `cargo` + +```bash +# build and run `beavercds check-access` with the config in the test repo +$ (cd tests/repo/ && cargo run -- check-access --profile test) +``` + +## Documentation + +Our documentation is available at [https://beavercds.info] built from source +under `docs/`. + +Dependencies (can be installed via `mise`): +- `node` @ lts +- `pnpm` (should be handled by node's corepack) + +```bash +cd docs/ + +mise install # for node/pnpm +pnpm install # install vitepress + +pnpm docs:build # build only +pnpm docs:dev # build and serve +``` diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..1170717 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,136 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# vitepress build output +**/.vitepress/dist + +# vitepress cache directory +**/.vitepress/cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts new file mode 100644 index 0000000..8d00914 --- /dev/null +++ b/docs/.vitepress/config.mts @@ -0,0 +1,73 @@ +import { defineConfig } from "vitepress"; +import { generateSidebar } from "vitepress-sidebar"; + +// https://vitepress.dev/reference/site-config +export default defineConfig({ + title: "beaverCDS Docs", + description: "Next-generation CTF deployment framework", + themeConfig: { + // https://vitepress.dev/reference/default-theme-config + nav: [ + { text: "Setup", link: "guides/infra-quickstart" }, + { + text: "Guides", + items: [ + { text: "Deployment Quickstart", link: "for-sysadmins/quickstart" }, + { text: "Add new challenge", link: "for-authors/quickstart" }, + ], + }, + + { + text: "Infrastructure Setup", + items: [ + { text: "Quickstart", link: "/for-sysadmins/quickstart" }, + { text: "Install", link: "/for-sysadmins/install" }, + { text: "Config Reference", link: "/for-sysadmins/config" }, + { text: "Architecture", link: "/for-sysadmins/architecture" }, + ], + }, + { + text: "Challenge Authors", + items: [ + { text: "Challenge Quickstart", link: "/for-authors/quickstart" }, + { + text: "Challenge Config Reference", + link: "/for-authors/challenge-config", + }, + ], + }, + ], + + // auto generate sidebar from directory structure, via vitepress-sidebar + sidebar: generateSidebar({ + documentRootPath: "./", + // pull title from markdown not filename + useTitleFromFileHeading: true, + useTitleFromFrontmatter: true, + keepMarkdownSyntaxFromTitle: true, + useFolderTitleFromIndexFile: true, + // transform name to sentence case + hyphenToSpace: true, + underscoreToSpace: true, + // capitalizeEachWords: true, + + sortFolderTo: "bottom", + sortMenusByFrontmatterOrder: true, + }), + + socialLinks: [ + { icon: "github", link: "https://github.com/osusec/beavercds-ng" }, + ], + }, + + // disable interpolation of {{ and }} in markdown + markdown: { + config(md) { + const defaultCodeInline = md.renderer.rules.code_inline!; + md.renderer.rules.code_inline = (tokens, idx, options, env, self) => { + tokens[idx].attrSet("v-pre", ""); + return defaultCodeInline(tokens, idx, options, env, self); + }; + }, + }, +}); diff --git a/docs/for-challenge-authors.md b/docs/for-challenge-authors.md deleted file mode 100644 index f9f13c7..0000000 --- a/docs/for-challenge-authors.md +++ /dev/null @@ -1,199 +0,0 @@ -# How to write beaverCDS challenge.yaml config - -tldr: see [the TCP example](#full-tcp-example) or [the web example](#full-http-example). - -### Metadata - -Self explanatory. - -```yaml -name: yet another pyjail -author: somebody, John Author -``` - -### Description - -Challenge description supports markdown and Jinja-style templating for challenge info. -The Jinja template fields available are: - -| Field name | Description | -| ----------- | ----------- | -| `hostname` | The hostname or domain for the challenge -| `port` | The port that the challenge is listening on -| `nc` | Insert the `nc` command to connect to TCP challenges (`nc {{hostname}} {{port}}`) -| `link` | Create a Markdown link to the exposed hostname/port -| `url` | The URL from `link` without the accompanying Markdown -| `challenge` | The full challenge.yaml config for this challenge, with subfields - -You probably only want `{{ nc }}` or `{{ link }}`. - -Example: - -```yaml -description: | - Some example challenge. Blah blah blah flavor text. - - In case you missed it, this was written by {{ challenge.author }} - and is called {{ challenge.name }}. - - {{ link }} # -becomes-> [example.chals.thectf.com](https://example.chals.thectf.com) - {{ nc }} # -becomes-> `nc example.chals.thectf.com 12345` -``` - - -### Flag - -Read flag from file: - -```yaml -flag: - file: ./flag -``` - -### Pods - -Defines how any container images for this challenge are built and deployed. - -The pod `name` is also used for extracting files, see [Providing files to -users](). - -`build` works similar to [Docker Compose](https://docs.docker.com/reference/compose-file/build/#illustrative-example), -either: - - a string path to the build context folder - - yaml with explicit `context` path, `dockerfile` path within context folder, and `args` build args \ - (only `context`, `dockerfile`, and `args` are supported for the detailed form) - -`ports` controls how the container is exposed. This should be a list of what port the container is listening, and how -that port should be exposed to players: -- For TCP challenges, set `expose.tcp` to the subdomain and port: `:` -- For HTTP challenges, set `expose.http` to the subdomain only: `` \ - The website domain will automatically be set up with an HTTPS cert. - - -```yaml -pods: - - name: tcp-example - build: . - replicas: 2 - ports: - - internal: 31337 - expose: - tcp: thechal:30124 # exposed at thechal.:30124 - - - name: web-example - build: - context: src/ - dockerfile: Containerfile - replicas: 2 - ports: - - internal: 31337 - expose: - http: webchal # exposed at https://webchal. -``` - - - - -This can be omitted if there are no containers for the challenge. - -### Providing files to users - -Files to give to players as downloads in frontend. - -These can be from the challenge folder in the repository, or from the -challenge's built container. These can also be zipped together into one file, or -uploaded separately. These need to be files, directories or globs are not (yet) -supported. - -This can be omitted if there are no files provided. - -```yaml -provide: - # file from the challenge folder in the repo - - somefile.txt - - # multiple files from chal_folder/src/, zipped as together.zip - - as: together.zip - include: - - src/file1 - - src/file2 - - src/file3 - - # extract these files from inside of the container image - # for the `main` pod (see previous section) - - from: main - include: - - /chal/notsh - - /lib/x86_64-linux-gnu/libc.so.6 - - # same as above, but now zipped together - - from: main - as: notsh.zip - include: - - /chal/notsh - - /lib/x86_64-linux-gnu/libc.so.6 -``` - - - - - -# Examples - -## Full TCP example - -```yaml -name: notsh -author: John Author -description: |- - This challenge isn't a shell - - {{ nc }} - -provide: - - from: main - include: - - /chal/notsh - - /lib/x86_64-linux-gnu/libc.so.6 - -flag: - file: ./flag - -pods: - - name: main - build: . - replicas: 2 - ports: - - internal: 31337 - expose: - tcp: 30124 -``` - -## Full HTTP example - -```yaml -name: bar -author: somebody -description: | - can you order a drink from the webserver? - - {{ url }} - -difficulty: 1 - -flag: - file: ./flag - -# no provide: section needed if no files - -pods: - - name: bar - build: - context: . - dockerfile: Containerfile - replicas: 1 - ports: - - internal: 80 - expose: - http: bar # subdomain only -``` diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..647d9b1 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,24 @@ +# Getting Started + +## Installation + +Install `beavercds` via Cargo or install from source: + +## Via `cargo` + +`beavercds` is not published to Crates.io (yet), you will need to install +directly from git: + +```sh +cargo install --git https://github.com/osusec/beavercds-ng +``` + +## Future plans + +We do not provide releases with precompiled binaries yet, but we intend to in +the future so that users will not need to compile from git source. + +## Next Steps + +After installing the `beavercds` binary, get started with [setting up the +challenges repo](/guides/infra-quickstart) or [writing challenges](/guides/challenge-quickstart). diff --git a/docs/guides/challenge-quickstart.md b/docs/guides/challenge-quickstart.md new file mode 100644 index 0000000..3bae004 --- /dev/null +++ b/docs/guides/challenge-quickstart.md @@ -0,0 +1,244 @@ +# Challenge Quickstart + +This will walk through the steps needed to create a new challenge and configure +its `challenge.yaml` config file. This is what sets all of the information about +the challenge and how it is presented to players. + +For the impatient, there are a few [complete example config files](#examples) at +the bottom of the page. + +::: info +We have an `new-chal` command planned that will prompt for most of this +information, but for now the challenge config needs to be created manually. +::: + + +## 1. Create challenge directory + +Challenges are expected to follow the directory structure of +`///challenge.yaml`. Create the directory and challenge config +file following that convention: + +```sh +$ mkdir -p $CATEGORY/$CHAL_NAME +$ touch $CATEGORY/$CHAL_NAME/challenge.yaml +``` + + +## 2. Add metadata + +Add the name, author, and description fields for your challenge to the `challenge.yaml` file. These will be shown to players on the scoreboard. + +```yaml [challenge.yaml] +name: My First Pyjail 🙂 +author: John Author +description: | + how will you get out of this one? + + {{ nc }} +``` + +The challenge name and author will be shown to players as written. + +The description supports Markdown formatting and Jinja-style templating for challenge info. Most of the time, you will need either `{{ nc }}` for netcat challenges or `{{ link }}` for web challenges. The full list of template options are available in [the reference](/reference/challenge-yaml-reference#description). + + +## 3. Set the flag + +If the flag for your challenge is stored in a file, read it in with: + +```yaml [challenge.yaml] +flag: + file: ./flag +``` + +For static file challenges or others that don't need it to be on disk + +```yaml [challenge.yaml] +flag: example{s0m3-fl4g} +``` + + +## 4. Create container and pod + +::: tip +If your challenge **does not** need a container, skip this section. +::: + +If your challenge has a service that is exposed to players, it needs to run as a +container. BeaverCDS will take care of building and deploying the container, but +you need to create the `Dockerfile` to build your challenge source into one. + +The pod `name` should be kept to one word, and is used to reference this +container for [providing files to users](#_5-provide-files-to-users) in the next section. + +### Build the container + +Once you have the container source and Dockerfile ready, add them to the pod +`build` section. +This is a good starting point for most challenges, where the Dockerfile and container source are in the same folder as this `challenge.yaml`: + +```yaml [challenge.yaml] {4-5} +pods: + - name: main + build: + context: . + dockerfile: Dockerfile + replicas: 2 + ports: + # todo +``` + +### Define how the challenge is exposed + +To expose the container to players, fill in the `ports` section with the +port the container is listening on, and either a port number or http subdomain. + +- For TCP challenges, set `expose.tcp` to a port. This must be a unique port + from other TCP challenges. The challenge will be exposed at + `:`. +- For web challenges, set `expose.http` to a subdomain. The challenge will be + exposed at `.` with an HTTPS cert. + +::: code-group +```yaml [For TCP challenges] {8-11} +pods: + - name: main + build: + context: . + dockerfile: Dockerfile + replicas: 2 + ports: + - internal: 31337 # <-- what your container listens on + expose: + tcp: 30124 # <-- would expose at chals.example.ctf:30124 +``` + +```yaml [For web challenges] {8-11} +pods: + - name: main + build: + context: . + dockerfile: Dockerfile + replicas: 2 + ports: + - internal: 31337 # <-- what your container listens on + expose: + http: my-chal # <-- would expose at https://my-chal.chals.example.ctf +``` +::: + + + +### 5. Provide files to users + +::: tip +If your challenge **does not** have any file handouts, skip this section. +::: + +If your challenge needs to provide files to users, add a `provide` block that +lists what files and where to find them. + +::: warning +Currently, all `provide` entries need to be files. Directories or globs are not +yet supported. +::: + +```yaml [challenge.yaml] +provide: + # file from the challenge folder in the repo + - somefile.txt + + # multiple files from src/ in the challenge folder, zipped as together.zip + - as: together.zip + include: + - src/foo + - src/bar + - src/baz + + # multiple files pulled from the container image for the `main` pod + # (see previous Pods section) + - from: main + include: + - /chal/notsh + - /lib/x86_64-linux-gnu/libc.so.6 + + # same as above, but now zipped together + - from: main + as: notsh.zip + include: + - /chal/notsh + - /lib/x86_64-linux-gnu/libc.so.6 +``` + + +## Examples + +::: code-group + +```yaml [Full TCP challenge] +name: notsh +author: John Author +description: |- + This challenge isn't a shell + + {{ nc }} + +flag: + file: ./flag + +provide: + - from: main + include: + - /chal/notsh + - /lib/x86_64-linux-gnu/libc.so.6 + +pods: + - name: main + build: . + replicas: 2 + ports: + - internal: 31337 + expose: + tcp: 30124 +``` + +```yaml [Full HTTP challenge] +name: bar +author: somebody +description: | + can you order a drink from the webserver? + + {{ url }} + +flag: + file: ./flag + +# no file handouts, so no provide: section + +pods: + - name: bar + build: + context: . + dockerfile: Containerfile + replicas: 1 + ports: + - internal: 80 + expose: + http: bar # subdomain only +``` + +```yaml [Full file-only challenge] +name: GhostINT +author: The Guessers Geo +description: | + where was this picture taken? + + flag format: `example{LAT__LONG}`, rounded to five places + +flag: "example{51.51771__-0.20495}" + +provide: + - the-view-from-your-balcony.png +``` +::: diff --git a/docs/guides/index.md b/docs/guides/index.md new file mode 100644 index 0000000..5bf01e8 --- /dev/null +++ b/docs/guides/index.md @@ -0,0 +1,3 @@ +--- +title: Guides +--- diff --git a/docs/guides/infra-quickstart.md b/docs/guides/infra-quickstart.md new file mode 100644 index 0000000..836e488 --- /dev/null +++ b/docs/guides/infra-quickstart.md @@ -0,0 +1,4 @@ +quick install instructions +requirements - cluster, registry, bucket +cluster-setup +configuring rcds.yaml diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..f6ef3bd --- /dev/null +++ b/docs/index.md @@ -0,0 +1,32 @@ +--- +# https://vitepress.dev/reference/default-theme-home-page +layout: home + +hero: + name: "beaverCDS" + text: "The next-generation CTF deployment framework" + tagline: "Challenge management made simple." + actions: + - theme: brand + text: Get started + link: /getting-started + - theme: alt + text: Add a challenge + link: /guides/challenge-quickstart + +features: + - title: Quick setup + icon: 🚀 + details: beaverCDS can get you from a fresh cluster to ready for challenges in minutes. + link: /getting-started + + - title: Control Challenge Deployment + icon: ⚙️ + details: Quickly enable or disable challenges to control exactly what is live for players. + link: /reference/config-reference + + - title: Simple yet flexible + icon: ✏️ + details: Adding a new challenge is simpler than ever. + link: /guides/challenge-quickstart +--- diff --git a/docs/mise.toml b/docs/mise.toml new file mode 100644 index 0000000..7ac45e7 --- /dev/null +++ b/docs/mise.toml @@ -0,0 +1,3 @@ +[tools] +node = "lts" +"npm:corepack" = "latest" diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 0000000..e06b358 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,22 @@ +{ + "name": "@beavercds/docs", + "private": true, + "type": "module", + "devDependencies": { + "vitepress": "1.6.3", + "vitepress-sidebar": "^1.31.1" + }, + "scripts": { + "docs:dev": "vitepress dev", + "docs:build": "vitepress build", + "docs:preview": "vitepress preview" + }, + "dependencies": { + "@types/markdown-it": "^14.1.2", + "js-toml": "^1.0.1", + "markdown-it": "^14.1.0", + "vitepress-plugin-group-icons": "^1.3.5", + "vitepress-plugin-tabs": "^0.7.0" + }, + "packageManager": "pnpm@9.15.3+sha512.1f79bc245a66eb0b07c5d4d83131240774642caaa86ef7d0434ab47c0d16f66b04e21e0c086eb61e62c77efc4d7f7ec071afad3796af64892fae66509173893a" +} diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml new file mode 100644 index 0000000..3b1954b --- /dev/null +++ b/docs/pnpm-lock.yaml @@ -0,0 +1,2223 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@types/markdown-it': + specifier: ^14.1.2 + version: 14.1.2 + js-toml: + specifier: ^1.0.1 + version: 1.0.1 + markdown-it: + specifier: ^14.1.0 + version: 14.1.0 + vitepress-plugin-group-icons: + specifier: ^1.3.5 + version: 1.5.5(markdown-it@14.1.0)(vite@5.4.19) + vitepress-plugin-tabs: + specifier: ^0.7.0 + version: 0.7.1(vitepress@1.6.3(@algolia/client-search@5.25.0)(postcss@8.5.3)(search-insights@2.17.3))(vue@3.5.14) + devDependencies: + vitepress: + specifier: 1.6.3 + version: 1.6.3(@algolia/client-search@5.25.0)(postcss@8.5.3)(search-insights@2.17.3) + vitepress-sidebar: + specifier: ^1.31.1 + version: 1.31.1 + +packages: + + '@algolia/autocomplete-core@1.17.7': + resolution: {integrity: sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==} + + '@algolia/autocomplete-plugin-algolia-insights@1.17.7': + resolution: {integrity: sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==} + peerDependencies: + search-insights: '>= 1 < 3' + + '@algolia/autocomplete-preset-algolia@1.17.7': + resolution: {integrity: sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + + '@algolia/autocomplete-shared@1.17.7': + resolution: {integrity: sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + + '@algolia/client-abtesting@5.25.0': + resolution: {integrity: sha512-1pfQulNUYNf1Tk/svbfjfkLBS36zsuph6m+B6gDkPEivFmso/XnRgwDvjAx80WNtiHnmeNjIXdF7Gos8+OLHqQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-analytics@5.25.0': + resolution: {integrity: sha512-AFbG6VDJX/o2vDd9hqncj1B6B4Tulk61mY0pzTtzKClyTDlNP0xaUiEKhl6E7KO9I/x0FJF5tDCm0Hn6v5x18A==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-common@5.25.0': + resolution: {integrity: sha512-il1zS/+Rc6la6RaCdSZ2YbJnkQC6W1wiBO8+SH+DE6CPMWBU6iDVzH0sCKSAtMWl9WBxoN6MhNjGBnCv9Yy2bA==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-insights@5.25.0': + resolution: {integrity: sha512-blbjrUH1siZNfyCGeq0iLQu00w3a4fBXm0WRIM0V8alcAPo7rWjLbMJMrfBtzL9X5ic6wgxVpDADXduGtdrnkw==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-personalization@5.25.0': + resolution: {integrity: sha512-aywoEuu1NxChBcHZ1pWaat0Plw7A8jDMwjgRJ00Mcl7wGlwuPt5dJ/LTNcg3McsEUbs2MBNmw0ignXBw9Tbgow==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-query-suggestions@5.25.0': + resolution: {integrity: sha512-a/W2z6XWKjKjIW1QQQV8PTTj1TXtaKx79uR3NGBdBdGvVdt24KzGAaN7sCr5oP8DW4D3cJt44wp2OY/fZcPAVA==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-search@5.25.0': + resolution: {integrity: sha512-9rUYcMIBOrCtYiLX49djyzxqdK9Dya/6Z/8sebPn94BekT+KLOpaZCuc6s0Fpfq7nx5J6YY5LIVFQrtioK9u0g==} + engines: {node: '>= 14.0.0'} + + '@algolia/ingestion@1.25.0': + resolution: {integrity: sha512-jJeH/Hk+k17Vkokf02lkfYE4A+EJX+UgnMhTLR/Mb+d1ya5WhE+po8p5a/Nxb6lo9OLCRl6w3Hmk1TX1e9gVbQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/monitoring@1.25.0': + resolution: {integrity: sha512-Ls3i1AehJ0C6xaHe7kK9vPmzImOn5zBg7Kzj8tRYIcmCWVyuuFwCIsbuIIz/qzUf1FPSWmw0TZrGeTumk2fqXg==} + engines: {node: '>= 14.0.0'} + + '@algolia/recommend@5.25.0': + resolution: {integrity: sha512-79sMdHpiRLXVxSjgw7Pt4R1aNUHxFLHiaTDnN2MQjHwJ1+o3wSseb55T9VXU4kqy3m7TUme3pyRhLk5ip/S4Mw==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-browser-xhr@5.25.0': + resolution: {integrity: sha512-JLaF23p1SOPBmfEqozUAgKHQrGl3z/Z5RHbggBu6s07QqXXcazEsub5VLonCxGVqTv6a61AAPr8J1G5HgGGjEw==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-fetch@5.25.0': + resolution: {integrity: sha512-rtzXwqzFi1edkOF6sXxq+HhmRKDy7tz84u0o5t1fXwz0cwx+cjpmxu/6OQKTdOJFS92JUYHsG51Iunie7xbqfQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-node-http@5.25.0': + resolution: {integrity: sha512-ZO0UKvDyEFvyeJQX0gmZDQEvhLZ2X10K+ps6hViMo1HgE2V8em00SwNsQ+7E/52a+YiBkVWX61pJJJE44juDMQ==} + engines: {node: '>= 14.0.0'} + + '@antfu/install-pkg@1.1.0': + resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + + '@antfu/utils@8.1.1': + resolution: {integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.27.2': + resolution: {integrity: sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/runtime-corejs3@7.27.1': + resolution: {integrity: sha512-909rVuj3phpjW6y0MCXAZ5iNeORePa6ldJvp2baWGcTjwqbBDDz6xoS5JHJ7lS88NlwLYj07ImL/8IUMtDZzTA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.27.1': + resolution: {integrity: sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==} + engines: {node: '>=6.9.0'} + + '@chevrotain/cst-dts-gen@11.0.3': + resolution: {integrity: sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==} + + '@chevrotain/gast@11.0.3': + resolution: {integrity: sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==} + + '@chevrotain/regexp-to-ast@11.0.3': + resolution: {integrity: sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==} + + '@chevrotain/types@11.0.3': + resolution: {integrity: sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==} + + '@chevrotain/utils@11.0.3': + resolution: {integrity: sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==} + + '@docsearch/css@3.8.2': + resolution: {integrity: sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==} + + '@docsearch/js@3.8.2': + resolution: {integrity: sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==} + + '@docsearch/react@3.8.2': + resolution: {integrity: sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==} + peerDependencies: + '@types/react': '>= 16.8.0 < 19.0.0' + react: '>= 16.8.0 < 19.0.0' + react-dom: '>= 16.8.0 < 19.0.0' + search-insights: '>= 1 < 3' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + react-dom: + optional: true + search-insights: + optional: true + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@iconify-json/logos@1.2.4': + resolution: {integrity: sha512-XC4If5D/hbaZvUkTV8iaZuGlQCyG6CNOlaAaJaGa13V5QMYwYjgtKk3vPP8wz3wtTVNVEVk3LRx1fOJz+YnSMw==} + + '@iconify-json/simple-icons@1.2.34': + resolution: {integrity: sha512-1FRWEA94hSl5zmBogRh6lQL36l7bVTfrl0n5+QJ+WmXmw70RccPT5phqeiSynwo3IhUWKoW2LiajyUMeweXW8g==} + + '@iconify-json/vscode-icons@1.2.21': + resolution: {integrity: sha512-velkIWAZRxvM9VuhkVeD6obyw0UXjTFk7lqcaxIzY+X7lXx2+yX2MoMbIwgpH3PbgqjvymS/SujBb4aWYcfmhw==} + + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/utils@2.3.0': + resolution: {integrity: sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@rollup/rollup-android-arm-eabi@4.41.0': + resolution: {integrity: sha512-KxN+zCjOYHGwCl4UCtSfZ6jrq/qi88JDUtiEFk8LELEHq2Egfc/FgW+jItZiOLRuQfb/3xJSgFuNPC9jzggX+A==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.41.0': + resolution: {integrity: sha512-yDvqx3lWlcugozax3DItKJI5j05B0d4Kvnjx+5mwiUpWramVvmAByYigMplaoAQ3pvdprGCTCE03eduqE/8mPQ==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.41.0': + resolution: {integrity: sha512-2KOU574vD3gzcPSjxO0eyR5iWlnxxtmW1F5CkNOHmMlueKNCQkxR6+ekgWyVnz6zaZihpUNkGxjsYrkTJKhkaw==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.41.0': + resolution: {integrity: sha512-gE5ACNSxHcEZyP2BA9TuTakfZvULEW4YAOtxl/A/YDbIir/wPKukde0BNPlnBiP88ecaN4BJI2TtAd+HKuZPQQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.41.0': + resolution: {integrity: sha512-GSxU6r5HnWij7FoSo7cZg3l5GPg4HFLkzsFFh0N/b16q5buW1NAWuCJ+HMtIdUEi6XF0qH+hN0TEd78laRp7Dg==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.41.0': + resolution: {integrity: sha512-KGiGKGDg8qLRyOWmk6IeiHJzsN/OYxO6nSbT0Vj4MwjS2XQy/5emsmtoqLAabqrohbgLWJ5GV3s/ljdrIr8Qjg==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.41.0': + resolution: {integrity: sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.41.0': + resolution: {integrity: sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.41.0': + resolution: {integrity: sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.41.0': + resolution: {integrity: sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.41.0': + resolution: {integrity: sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.41.0': + resolution: {integrity: sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.41.0': + resolution: {integrity: sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.41.0': + resolution: {integrity: sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.41.0': + resolution: {integrity: sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.41.0': + resolution: {integrity: sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.41.0': + resolution: {integrity: sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.41.0': + resolution: {integrity: sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.41.0': + resolution: {integrity: sha512-tmazCrAsKzdkXssEc65zIE1oC6xPHwfy9d5Ta25SRCDOZS+I6RypVVShWALNuU9bxIfGA0aqrmzlzoM5wO5SPQ==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.41.0': + resolution: {integrity: sha512-h1J+Yzjo/X+0EAvR2kIXJDuTuyT7drc+t2ALY0nIcGPbTatNOf0VWdhEA2Z4AAjv6X1NJV7SYo5oCTYRJhSlVA==} + cpu: [x64] + os: [win32] + + '@shikijs/core@2.5.0': + resolution: {integrity: sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==} + + '@shikijs/engine-javascript@2.5.0': + resolution: {integrity: sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==} + + '@shikijs/engine-oniguruma@2.5.0': + resolution: {integrity: sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==} + + '@shikijs/langs@2.5.0': + resolution: {integrity: sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==} + + '@shikijs/themes@2.5.0': + resolution: {integrity: sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==} + + '@shikijs/transformers@2.5.0': + resolution: {integrity: sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==} + + '@shikijs/types@2.5.0': + resolution: {integrity: sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/linkify-it@5.0.0': + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + + '@types/markdown-it@14.1.2': + resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/web-bluetooth@0.0.21': + resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@vitejs/plugin-vue@5.2.4': + resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 + vue: ^3.2.25 + + '@vue/compiler-core@3.5.14': + resolution: {integrity: sha512-k7qMHMbKvoCXIxPhquKQVw3Twid3Kg4s7+oYURxLGRd56LiuHJVrvFKI4fm2AM3c8apqODPfVJGoh8nePbXMRA==} + + '@vue/compiler-dom@3.5.14': + resolution: {integrity: sha512-1aOCSqxGOea5I80U2hQJvXYpPm/aXo95xL/m/mMhgyPUsKe9jhjwWpziNAw7tYRnbz1I61rd9Mld4W9KmmRoug==} + + '@vue/compiler-sfc@3.5.14': + resolution: {integrity: sha512-9T6m/9mMr81Lj58JpzsiSIjBgv2LiVoWjIVa7kuXHICUi8LiDSIotMpPRXYJsXKqyARrzjT24NAwttrMnMaCXA==} + + '@vue/compiler-ssr@3.5.14': + resolution: {integrity: sha512-Y0G7PcBxr1yllnHuS/NxNCSPWnRGH4Ogrp0tsLA5QemDZuJLs99YjAKQ7KqkHE0vCg4QTKlQzXLKCMF7WPSl7Q==} + + '@vue/devtools-api@7.7.6': + resolution: {integrity: sha512-b2Xx0KvXZObePpXPYHvBRRJLDQn5nhKjXh7vUhMEtWxz1AYNFOVIsh5+HLP8xDGL7sy+Q7hXeUxPHB/KgbtsPw==} + + '@vue/devtools-kit@7.7.6': + resolution: {integrity: sha512-geu7ds7tem2Y7Wz+WgbnbZ6T5eadOvozHZ23Atk/8tksHMFOFylKi1xgGlQlVn0wlkEf4hu+vd5ctj1G4kFtwA==} + + '@vue/devtools-shared@7.7.6': + resolution: {integrity: sha512-yFEgJZ/WblEsojQQceuyK6FzpFDx4kqrz2ohInxNj5/DnhoX023upTv4OD6lNPLAA5LLkbwPVb10o/7b+Y4FVA==} + + '@vue/reactivity@3.5.14': + resolution: {integrity: sha512-7cK1Hp343Fu/SUCCO52vCabjvsYu7ZkOqyYu7bXV9P2yyfjUMUXHZafEbq244sP7gf+EZEz+77QixBTuEqkQQw==} + + '@vue/runtime-core@3.5.14': + resolution: {integrity: sha512-w9JWEANwHXNgieAhxPpEpJa+0V5G0hz3NmjAZwlOebtfKyp2hKxKF0+qSh0Xs6/PhfGihuSdqMprMVcQU/E6ag==} + + '@vue/runtime-dom@3.5.14': + resolution: {integrity: sha512-lCfR++IakeI35TVR80QgOelsUIdcKjd65rWAMfdSlCYnaEY5t3hYwru7vvcWaqmrK+LpI7ZDDYiGU5V3xjMacw==} + + '@vue/server-renderer@3.5.14': + resolution: {integrity: sha512-Rf/ISLqokIvcySIYnv3tNWq40PLpNLDLSJwwVWzG6MNtyIhfbcrAxo5ZL9nARJhqjZyWWa40oRb2IDuejeuv6w==} + peerDependencies: + vue: 3.5.14 + + '@vue/shared@3.5.14': + resolution: {integrity: sha512-oXTwNxVfc9EtP1zzXAlSlgARLXNC84frFYkS0HHz0h3E4WZSP9sywqjqzGCP9Y34M8ipNmd380pVgmMuwELDyQ==} + + '@vueuse/core@12.8.2': + resolution: {integrity: sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==} + + '@vueuse/integrations@12.8.2': + resolution: {integrity: sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==} + peerDependencies: + async-validator: ^4 + axios: ^1 + change-case: ^5 + drauu: ^0.4 + focus-trap: ^7 + fuse.js: ^7 + idb-keyval: ^6 + jwt-decode: ^4 + nprogress: ^0.2 + qrcode: ^1.5 + sortablejs: ^1 + universal-cookie: ^7 + peerDependenciesMeta: + async-validator: + optional: true + axios: + optional: true + change-case: + optional: true + drauu: + optional: true + focus-trap: + optional: true + fuse.js: + optional: true + idb-keyval: + optional: true + jwt-decode: + optional: true + nprogress: + optional: true + qrcode: + optional: true + sortablejs: + optional: true + universal-cookie: + optional: true + + '@vueuse/metadata@12.8.2': + resolution: {integrity: sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==} + + '@vueuse/shared@12.8.2': + resolution: {integrity: sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==} + + acorn@8.14.1: + resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} + engines: {node: '>=0.4.0'} + hasBin: true + + algoliasearch@5.25.0: + resolution: {integrity: sha512-n73BVorL4HIwKlfJKb4SEzAYkR3Buwfwbh+MYxg2mloFph2fFGV58E90QTzdbfzWrLn4HE5Czx/WTjI8fcHaMg==} + engines: {node: '>= 14.0.0'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + birpc@2.3.0: + resolution: {integrity: sha512-ijbtkn/F3Pvzb6jHypHRyve2QApOCZDR25D/VnkY2G/lBNcXCTsnsCxgY4k4PkVB7zfwzYbY3O9Lcqe3xufS5g==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + chevrotain@11.0.3: + resolution: {integrity: sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + confbox@0.2.2: + resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + + copy-anything@3.0.5: + resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} + engines: {node: '>=12.13'} + + core-js-pure@3.42.0: + resolution: {integrity: sha512-007bM04u91fF4kMgwom2I5cQxAFIy8jVulgr9eozILl/SZE53QOqnW/+vviC+wQWLv+AunBG+8Q0TLoeSsSxRQ==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + emoji-regex-xs@1.0.0: + resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + exsolve@1.0.5: + resolution: {integrity: sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==} + + extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + + focus-trap@7.6.4: + resolution: {integrity: sha512-xx560wGBk7seZ6y933idtjJQc1l+ck+pI3sKvhKozdBV1dRZoKhkW5xoCaFv9tQiX5RH1xfSxjuNu6g+lmN/gw==} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + + gray-matter@4.0.3: + resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} + engines: {node: '>=6.0'} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-what@4.1.16: + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + engines: {node: '>=12.13'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + js-toml@1.0.1: + resolution: {integrity: sha512-rHd/IolpFm2V5BmHCEY8CckHs8NDsYZZ64H5RNgA6Opsr9vX4QyTiQPplgtqg7b3ztqYShZC38nl6CUg7QuhXg==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + + local-pkg@1.1.1: + resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==} + engines: {node: '>=14'} + + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + mark.js@8.11.1: + resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} + + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minisearch@7.1.2: + resolution: {integrity: sha512-R1Pd9eF+MD5JYDDSPAp/q1ougKglm14uEkPMvQ/05RGmx6G9wvmLTrTI/Q5iPNJLYqNdsDQ7qTGIcNWR+FrHmA==} + + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + + mlly@1.7.4: + resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + oniguruma-to-es@3.1.1: + resolution: {integrity: sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + package-manager-detector@1.3.0: + resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + pkg-types@2.1.0: + resolution: {integrity: sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==} + + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + engines: {node: ^10 || ^12 || >=14} + + preact@10.26.6: + resolution: {integrity: sha512-5SRRBinwpwkaD+OqlBDeITlRgvd8I8QlxHJw9AxSdMNV6O+LodN9nUyYGpSF7sadHjs6RzeFShMexC6DbtWr9g==} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + + qsu@1.10.0: + resolution: {integrity: sha512-60UGE7IEYXX/xy/n1w7vDm+is43pmePajAdXAnFOczvVDJKbVqZ5/RvRy+yobjA4iQitr4H/4zojVFtAkNWW9g==} + engines: {node: '>=18.0.0'} + + quansync@0.2.10: + resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.0.1: + resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rollup@4.41.0: + resolution: {integrity: sha512-HqMFpUbWlf/tvcxBFNKnJyzc7Lk+XO3FGc3pbNBLqEbOz0gPLRgcrlS3UF4MfUrVlstOaP/q0kM6GVvi+LrLRg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + search-insights@2.17.3: + resolution: {integrity: sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==} + + section-matter@1.0.0: + resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} + engines: {node: '>=4'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shiki@2.5.0: + resolution: {integrity: sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + speakingurl@14.0.1: + resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} + engines: {node: '>=0.10.0'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom-string@1.0.0: + resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} + engines: {node: '>=0.10.0'} + + superjson@2.2.2: + resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==} + engines: {node: '>=16'} + + tabbable@6.2.0: + resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + + tinyexec@1.0.1: + resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + + unist-util-is@6.0.0: + resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + + vfile-message@4.0.2: + resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite@5.4.19: + resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vitepress-plugin-group-icons@1.5.5: + resolution: {integrity: sha512-eVnBL3lVOYxByQg5xo44QZtGPv41JyxWI7YxuwrGcNUU+W8MMIjb9XlivBXb3W8CosFllJlLGiqNCBTnFZHFTA==} + peerDependencies: + markdown-it: '>=14' + vite: '>=3' + + vitepress-plugin-tabs@0.7.1: + resolution: {integrity: sha512-jxJvsicxnMSIYX9b8mAFLD2nwyKUcMO10dEt4nDSbinZhM8cGvAmMFOHPdf6TBX6gYZRl+/++/iYHtoM14fERQ==} + peerDependencies: + vitepress: ^1.0.0 + vue: ^3.5.0 + + vitepress-sidebar@1.31.1: + resolution: {integrity: sha512-Hx10z5le87jIIXVfKq4AtRrVqVJJ/1cQsZhmwT+ghVR/j4Yor9FjNMszyigJ54ktrEtoxSLO6C9tvuLauT4lZA==} + engines: {node: '>=18.0.0'} + + vitepress@1.6.3: + resolution: {integrity: sha512-fCkfdOk8yRZT8GD9BFqusW3+GggWYZ/rYncOfmgcDtP3ualNHCAg+Robxp2/6xfH1WwPHtGpPwv7mbA3qomtBw==} + hasBin: true + peerDependencies: + markdown-it-mathjax3: ^4 + postcss: ^8 + peerDependenciesMeta: + markdown-it-mathjax3: + optional: true + postcss: + optional: true + + vue@3.5.14: + resolution: {integrity: sha512-LbOm50/vZFG6Mhy6KscQYXZMQ0LMCC/y40HDJPPvGFQ+i/lUH+PJHR6C3assgOQiXdl6tAfsXHbXYVBZZu65ew==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + xregexp@5.1.2: + resolution: {integrity: sha512-6hGgEMCGhqCTFEJbqmWrNIPqfpdirdGWkqshu7fFZddmTSfgv5Sn9D2SaKloR79s5VUiUlpwzg3CM3G6D3VIlw==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.25.0)(algoliasearch@5.25.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.25.0)(algoliasearch@5.25.0)(search-insights@2.17.3) + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.25.0)(algoliasearch@5.25.0) + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + - search-insights + + '@algolia/autocomplete-plugin-algolia-insights@1.17.7(@algolia/client-search@5.25.0)(algoliasearch@5.25.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.25.0)(algoliasearch@5.25.0) + search-insights: 2.17.3 + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + + '@algolia/autocomplete-preset-algolia@1.17.7(@algolia/client-search@5.25.0)(algoliasearch@5.25.0)': + dependencies: + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.25.0)(algoliasearch@5.25.0) + '@algolia/client-search': 5.25.0 + algoliasearch: 5.25.0 + + '@algolia/autocomplete-shared@1.17.7(@algolia/client-search@5.25.0)(algoliasearch@5.25.0)': + dependencies: + '@algolia/client-search': 5.25.0 + algoliasearch: 5.25.0 + + '@algolia/client-abtesting@5.25.0': + dependencies: + '@algolia/client-common': 5.25.0 + '@algolia/requester-browser-xhr': 5.25.0 + '@algolia/requester-fetch': 5.25.0 + '@algolia/requester-node-http': 5.25.0 + + '@algolia/client-analytics@5.25.0': + dependencies: + '@algolia/client-common': 5.25.0 + '@algolia/requester-browser-xhr': 5.25.0 + '@algolia/requester-fetch': 5.25.0 + '@algolia/requester-node-http': 5.25.0 + + '@algolia/client-common@5.25.0': {} + + '@algolia/client-insights@5.25.0': + dependencies: + '@algolia/client-common': 5.25.0 + '@algolia/requester-browser-xhr': 5.25.0 + '@algolia/requester-fetch': 5.25.0 + '@algolia/requester-node-http': 5.25.0 + + '@algolia/client-personalization@5.25.0': + dependencies: + '@algolia/client-common': 5.25.0 + '@algolia/requester-browser-xhr': 5.25.0 + '@algolia/requester-fetch': 5.25.0 + '@algolia/requester-node-http': 5.25.0 + + '@algolia/client-query-suggestions@5.25.0': + dependencies: + '@algolia/client-common': 5.25.0 + '@algolia/requester-browser-xhr': 5.25.0 + '@algolia/requester-fetch': 5.25.0 + '@algolia/requester-node-http': 5.25.0 + + '@algolia/client-search@5.25.0': + dependencies: + '@algolia/client-common': 5.25.0 + '@algolia/requester-browser-xhr': 5.25.0 + '@algolia/requester-fetch': 5.25.0 + '@algolia/requester-node-http': 5.25.0 + + '@algolia/ingestion@1.25.0': + dependencies: + '@algolia/client-common': 5.25.0 + '@algolia/requester-browser-xhr': 5.25.0 + '@algolia/requester-fetch': 5.25.0 + '@algolia/requester-node-http': 5.25.0 + + '@algolia/monitoring@1.25.0': + dependencies: + '@algolia/client-common': 5.25.0 + '@algolia/requester-browser-xhr': 5.25.0 + '@algolia/requester-fetch': 5.25.0 + '@algolia/requester-node-http': 5.25.0 + + '@algolia/recommend@5.25.0': + dependencies: + '@algolia/client-common': 5.25.0 + '@algolia/requester-browser-xhr': 5.25.0 + '@algolia/requester-fetch': 5.25.0 + '@algolia/requester-node-http': 5.25.0 + + '@algolia/requester-browser-xhr@5.25.0': + dependencies: + '@algolia/client-common': 5.25.0 + + '@algolia/requester-fetch@5.25.0': + dependencies: + '@algolia/client-common': 5.25.0 + + '@algolia/requester-node-http@5.25.0': + dependencies: + '@algolia/client-common': 5.25.0 + + '@antfu/install-pkg@1.1.0': + dependencies: + package-manager-detector: 1.3.0 + tinyexec: 1.0.1 + + '@antfu/utils@8.1.1': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/parser@7.27.2': + dependencies: + '@babel/types': 7.27.1 + + '@babel/runtime-corejs3@7.27.1': + dependencies: + core-js-pure: 3.42.0 + + '@babel/types@7.27.1': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@chevrotain/cst-dts-gen@11.0.3': + dependencies: + '@chevrotain/gast': 11.0.3 + '@chevrotain/types': 11.0.3 + lodash-es: 4.17.21 + + '@chevrotain/gast@11.0.3': + dependencies: + '@chevrotain/types': 11.0.3 + lodash-es: 4.17.21 + + '@chevrotain/regexp-to-ast@11.0.3': {} + + '@chevrotain/types@11.0.3': {} + + '@chevrotain/utils@11.0.3': {} + + '@docsearch/css@3.8.2': {} + + '@docsearch/js@3.8.2(@algolia/client-search@5.25.0)(search-insights@2.17.3)': + dependencies: + '@docsearch/react': 3.8.2(@algolia/client-search@5.25.0)(search-insights@2.17.3) + preact: 10.26.6 + transitivePeerDependencies: + - '@algolia/client-search' + - '@types/react' + - react + - react-dom + - search-insights + + '@docsearch/react@3.8.2(@algolia/client-search@5.25.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.25.0)(algoliasearch@5.25.0)(search-insights@2.17.3) + '@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.25.0)(algoliasearch@5.25.0) + '@docsearch/css': 3.8.2 + algoliasearch: 5.25.0 + optionalDependencies: + search-insights: 2.17.3 + transitivePeerDependencies: + - '@algolia/client-search' + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@iconify-json/logos@1.2.4': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify-json/simple-icons@1.2.34': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify-json/vscode-icons@1.2.21': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify/types@2.0.0': {} + + '@iconify/utils@2.3.0': + dependencies: + '@antfu/install-pkg': 1.1.0 + '@antfu/utils': 8.1.1 + '@iconify/types': 2.0.0 + debug: 4.4.1 + globals: 15.15.0 + kolorist: 1.8.0 + local-pkg: 1.1.1 + mlly: 1.7.4 + transitivePeerDependencies: + - supports-color + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@rollup/rollup-android-arm-eabi@4.41.0': + optional: true + + '@rollup/rollup-android-arm64@4.41.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.41.0': + optional: true + + '@rollup/rollup-darwin-x64@4.41.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.41.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.41.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.41.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.41.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.41.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.41.0': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.41.0': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.41.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.41.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.41.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.41.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.41.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.41.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.41.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.41.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.41.0': + optional: true + + '@shikijs/core@2.5.0': + dependencies: + '@shikijs/engine-javascript': 2.5.0 + '@shikijs/engine-oniguruma': 2.5.0 + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 3.1.1 + + '@shikijs/engine-oniguruma@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + + '@shikijs/themes@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + + '@shikijs/transformers@2.5.0': + dependencies: + '@shikijs/core': 2.5.0 + '@shikijs/types': 2.5.0 + + '@shikijs/types@2.5.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@types/estree@1.0.7': {} + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/linkify-it@5.0.0': {} + + '@types/markdown-it@14.1.2': + dependencies: + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdurl@2.0.0': {} + + '@types/unist@3.0.3': {} + + '@types/web-bluetooth@0.0.21': {} + + '@ungap/structured-clone@1.3.0': {} + + '@vitejs/plugin-vue@5.2.4(vite@5.4.19)(vue@3.5.14)': + dependencies: + vite: 5.4.19 + vue: 3.5.14 + + '@vue/compiler-core@3.5.14': + dependencies: + '@babel/parser': 7.27.2 + '@vue/shared': 3.5.14 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.14': + dependencies: + '@vue/compiler-core': 3.5.14 + '@vue/shared': 3.5.14 + + '@vue/compiler-sfc@3.5.14': + dependencies: + '@babel/parser': 7.27.2 + '@vue/compiler-core': 3.5.14 + '@vue/compiler-dom': 3.5.14 + '@vue/compiler-ssr': 3.5.14 + '@vue/shared': 3.5.14 + estree-walker: 2.0.2 + magic-string: 0.30.17 + postcss: 8.5.3 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.14': + dependencies: + '@vue/compiler-dom': 3.5.14 + '@vue/shared': 3.5.14 + + '@vue/devtools-api@7.7.6': + dependencies: + '@vue/devtools-kit': 7.7.6 + + '@vue/devtools-kit@7.7.6': + dependencies: + '@vue/devtools-shared': 7.7.6 + birpc: 2.3.0 + hookable: 5.5.3 + mitt: 3.0.1 + perfect-debounce: 1.0.0 + speakingurl: 14.0.1 + superjson: 2.2.2 + + '@vue/devtools-shared@7.7.6': + dependencies: + rfdc: 1.4.1 + + '@vue/reactivity@3.5.14': + dependencies: + '@vue/shared': 3.5.14 + + '@vue/runtime-core@3.5.14': + dependencies: + '@vue/reactivity': 3.5.14 + '@vue/shared': 3.5.14 + + '@vue/runtime-dom@3.5.14': + dependencies: + '@vue/reactivity': 3.5.14 + '@vue/runtime-core': 3.5.14 + '@vue/shared': 3.5.14 + csstype: 3.1.3 + + '@vue/server-renderer@3.5.14(vue@3.5.14)': + dependencies: + '@vue/compiler-ssr': 3.5.14 + '@vue/shared': 3.5.14 + vue: 3.5.14 + + '@vue/shared@3.5.14': {} + + '@vueuse/core@12.8.2': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 12.8.2 + '@vueuse/shared': 12.8.2 + vue: 3.5.14 + transitivePeerDependencies: + - typescript + + '@vueuse/integrations@12.8.2(focus-trap@7.6.4)': + dependencies: + '@vueuse/core': 12.8.2 + '@vueuse/shared': 12.8.2 + vue: 3.5.14 + optionalDependencies: + focus-trap: 7.6.4 + transitivePeerDependencies: + - typescript + + '@vueuse/metadata@12.8.2': {} + + '@vueuse/shared@12.8.2': + dependencies: + vue: 3.5.14 + transitivePeerDependencies: + - typescript + + acorn@8.14.1: {} + + algoliasearch@5.25.0: + dependencies: + '@algolia/client-abtesting': 5.25.0 + '@algolia/client-analytics': 5.25.0 + '@algolia/client-common': 5.25.0 + '@algolia/client-insights': 5.25.0 + '@algolia/client-personalization': 5.25.0 + '@algolia/client-query-suggestions': 5.25.0 + '@algolia/client-search': 5.25.0 + '@algolia/ingestion': 1.25.0 + '@algolia/monitoring': 1.25.0 + '@algolia/recommend': 5.25.0 + '@algolia/requester-browser-xhr': 5.25.0 + '@algolia/requester-fetch': 5.25.0 + '@algolia/requester-node-http': 5.25.0 + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + balanced-match@1.0.2: {} + + birpc@2.3.0: {} + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + ccount@2.0.1: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + chevrotain@11.0.3: + dependencies: + '@chevrotain/cst-dts-gen': 11.0.3 + '@chevrotain/gast': 11.0.3 + '@chevrotain/regexp-to-ast': 11.0.3 + '@chevrotain/types': 11.0.3 + '@chevrotain/utils': 11.0.3 + lodash-es: 4.17.21 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + comma-separated-tokens@2.0.3: {} + + confbox@0.1.8: {} + + confbox@0.2.2: {} + + copy-anything@3.0.5: + dependencies: + is-what: 4.1.16 + + core-js-pure@3.42.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.1.3: {} + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + dequal@2.0.3: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + eastasianwidth@0.2.0: {} + + emoji-regex-xs@1.0.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + entities@4.5.0: {} + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + esprima@4.0.1: {} + + estree-walker@2.0.2: {} + + exsolve@1.0.5: {} + + extend-shallow@2.0.1: + dependencies: + is-extendable: 0.1.1 + + focus-trap@7.6.4: + dependencies: + tabbable: 6.2.0 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + fsevents@2.3.3: + optional: true + + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + globals@15.15.0: {} + + gray-matter@4.0.3: + dependencies: + js-yaml: 3.14.1 + kind-of: 6.0.3 + section-matter: 1.0.0 + strip-bom-string: 1.0.0 + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hookable@5.5.3: {} + + html-void-elements@3.0.0: {} + + is-extendable@0.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-what@4.1.16: {} + + isexe@2.0.0: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + js-toml@1.0.1: + dependencies: + chevrotain: 11.0.3 + xregexp: 5.1.2 + + js-yaml@3.14.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + kind-of@6.0.3: {} + + kolorist@1.8.0: {} + + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + + local-pkg@1.1.1: + dependencies: + mlly: 1.7.4 + pkg-types: 2.1.0 + quansync: 0.2.10 + + lodash-es@4.17.21: {} + + lru-cache@10.4.3: {} + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + mark.js@8.11.1: {} + + markdown-it@14.1.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + + mdast-util-to-hast@13.2.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + mdurl@2.0.0: {} + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-encode@2.0.1: {} + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + + minipass@7.1.2: {} + + minisearch@7.1.2: {} + + mitt@3.0.1: {} + + mlly@1.7.4: + dependencies: + acorn: 8.14.1 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.1 + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + oniguruma-to-es@3.1.1: + dependencies: + emoji-regex-xs: 1.0.0 + regex: 6.0.1 + regex-recursion: 6.0.2 + + package-json-from-dist@1.0.1: {} + + package-manager-detector@1.3.0: {} + + path-key@3.1.1: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + pathe@2.0.3: {} + + perfect-debounce@1.0.0: {} + + picocolors@1.1.1: {} + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.7.4 + pathe: 2.0.3 + + pkg-types@2.1.0: + dependencies: + confbox: 0.2.2 + exsolve: 1.0.5 + pathe: 2.0.3 + + postcss@8.5.3: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + preact@10.26.6: {} + + property-information@7.1.0: {} + + punycode.js@2.3.1: {} + + qsu@1.10.0: {} + + quansync@0.2.10: {} + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.0.1: + dependencies: + regex-utilities: 2.3.0 + + rfdc@1.4.1: {} + + rollup@4.41.0: + dependencies: + '@types/estree': 1.0.7 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.41.0 + '@rollup/rollup-android-arm64': 4.41.0 + '@rollup/rollup-darwin-arm64': 4.41.0 + '@rollup/rollup-darwin-x64': 4.41.0 + '@rollup/rollup-freebsd-arm64': 4.41.0 + '@rollup/rollup-freebsd-x64': 4.41.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.41.0 + '@rollup/rollup-linux-arm-musleabihf': 4.41.0 + '@rollup/rollup-linux-arm64-gnu': 4.41.0 + '@rollup/rollup-linux-arm64-musl': 4.41.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.41.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.41.0 + '@rollup/rollup-linux-riscv64-gnu': 4.41.0 + '@rollup/rollup-linux-riscv64-musl': 4.41.0 + '@rollup/rollup-linux-s390x-gnu': 4.41.0 + '@rollup/rollup-linux-x64-gnu': 4.41.0 + '@rollup/rollup-linux-x64-musl': 4.41.0 + '@rollup/rollup-win32-arm64-msvc': 4.41.0 + '@rollup/rollup-win32-ia32-msvc': 4.41.0 + '@rollup/rollup-win32-x64-msvc': 4.41.0 + fsevents: 2.3.3 + + search-insights@2.17.3: {} + + section-matter@1.0.0: + dependencies: + extend-shallow: 2.0.1 + kind-of: 6.0.3 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shiki@2.5.0: + dependencies: + '@shikijs/core': 2.5.0 + '@shikijs/engine-javascript': 2.5.0 + '@shikijs/engine-oniguruma': 2.5.0 + '@shikijs/langs': 2.5.0 + '@shikijs/themes': 2.5.0 + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + signal-exit@4.1.0: {} + + source-map-js@1.2.1: {} + + space-separated-tokens@2.0.2: {} + + speakingurl@14.0.1: {} + + sprintf-js@1.0.3: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-bom-string@1.0.0: {} + + superjson@2.2.2: + dependencies: + copy-anything: 3.0.5 + + tabbable@6.2.0: {} + + tinyexec@1.0.1: {} + + trim-lines@3.0.1: {} + + uc.micro@2.1.0: {} + + ufo@1.6.1: {} + + unist-util-is@6.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.1: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + vfile-message@4.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.2 + + vite@5.4.19: + dependencies: + esbuild: 0.21.5 + postcss: 8.5.3 + rollup: 4.41.0 + optionalDependencies: + fsevents: 2.3.3 + + vitepress-plugin-group-icons@1.5.5(markdown-it@14.1.0)(vite@5.4.19): + dependencies: + '@iconify-json/logos': 1.2.4 + '@iconify-json/vscode-icons': 1.2.21 + '@iconify/utils': 2.3.0 + markdown-it: 14.1.0 + vite: 5.4.19 + transitivePeerDependencies: + - supports-color + + vitepress-plugin-tabs@0.7.1(vitepress@1.6.3(@algolia/client-search@5.25.0)(postcss@8.5.3)(search-insights@2.17.3))(vue@3.5.14): + dependencies: + vitepress: 1.6.3(@algolia/client-search@5.25.0)(postcss@8.5.3)(search-insights@2.17.3) + vue: 3.5.14 + + vitepress-sidebar@1.31.1: + dependencies: + glob: 10.4.5 + gray-matter: 4.0.3 + qsu: 1.10.0 + + vitepress@1.6.3(@algolia/client-search@5.25.0)(postcss@8.5.3)(search-insights@2.17.3): + dependencies: + '@docsearch/css': 3.8.2 + '@docsearch/js': 3.8.2(@algolia/client-search@5.25.0)(search-insights@2.17.3) + '@iconify-json/simple-icons': 1.2.34 + '@shikijs/core': 2.5.0 + '@shikijs/transformers': 2.5.0 + '@shikijs/types': 2.5.0 + '@types/markdown-it': 14.1.2 + '@vitejs/plugin-vue': 5.2.4(vite@5.4.19)(vue@3.5.14) + '@vue/devtools-api': 7.7.6 + '@vue/shared': 3.5.14 + '@vueuse/core': 12.8.2 + '@vueuse/integrations': 12.8.2(focus-trap@7.6.4) + focus-trap: 7.6.4 + mark.js: 8.11.1 + minisearch: 7.1.2 + shiki: 2.5.0 + vite: 5.4.19 + vue: 3.5.14 + optionalDependencies: + postcss: 8.5.3 + transitivePeerDependencies: + - '@algolia/client-search' + - '@types/node' + - '@types/react' + - async-validator + - axios + - change-case + - drauu + - fuse.js + - idb-keyval + - jwt-decode + - less + - lightningcss + - nprogress + - qrcode + - react + - react-dom + - sass + - sass-embedded + - search-insights + - sortablejs + - stylus + - sugarss + - terser + - typescript + - universal-cookie + + vue@3.5.14: + dependencies: + '@vue/compiler-dom': 3.5.14 + '@vue/compiler-sfc': 3.5.14 + '@vue/runtime-dom': 3.5.14 + '@vue/server-renderer': 3.5.14(vue@3.5.14) + '@vue/shared': 3.5.14 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + xregexp@5.1.2: + dependencies: + '@babel/runtime-corejs3': 7.27.1 + + zwitch@2.0.4: {} diff --git a/docs/reference/challenge-repo-structure.md b/docs/reference/challenge-repo-structure.md new file mode 100644 index 0000000..c998df1 --- /dev/null +++ b/docs/reference/challenge-repo-structure.md @@ -0,0 +1,3 @@ +# Challenge Repo Structure + +expected layout of challenges repo diff --git a/docs/reference/challenge-yaml-reference.md b/docs/reference/challenge-yaml-reference.md new file mode 100644 index 0000000..45fcb2e --- /dev/null +++ b/docs/reference/challenge-yaml-reference.md @@ -0,0 +1,413 @@ +--- +title: challenge.yaml Reference +--- + +# `challenge.yaml` Config Reference + +Challenge configuration is expected to be at `//challenge.yaml`. + +There are some examples available on the [challenge quickstart guide](/guides/challenge-quickstart#examples). + +Available fields: + +[[toc]] + +`*` denotes required fields. + +## `name`* + +- type: `string` +- no default + +The name of the challenge, as shown to players in the frontend UI. Any +characters are allowed. + +```yaml +name: notsh +name: cha-cha-cha +# name can have spaces: +name: Revenge of the FIPS +``` + +## `author`* + +- type: `string` +- no default + +Author or authors of the challenge, as shown to players in the frontend UI. If +there are multiple authors, specify them as one string. + +```yaml +author: John Author +# multiple authors: +author: Alice, Bob, and others +``` + +## `description`* + +- type: `string` +- no default + +Description and flavortext for the challenge, as shown to players in the +frontend UI. Supports templating to include information about the challenge, +such as the link or command to connect. + +Most challenges only need `{{ nc }}` or `{{ link }}`. + +| Available fields | Description | +| ---------------- | ------------------------------------------------------------------------------------------------------------------------ | +| `domain` | Full domain the challenge is exposed at, e.g. `.chals.example.ctf` | +| `port` | Port the challenge is listening on | +| `nc` | `nc` command to connect to TCP challenges, with Markdown backticks
(equivalent to `` `nc {{domain}} {{port}}` ``) | +| `url` | URL to the exposed web domain for web challenges, plus port if needed
(equivalent to `https://{{domain}}:{{port}}`) | +| `link` | Markdown link to `url` | +| `challenge` | The full challenge.yaml config object for this challenge, with subfields | + +```yaml +description: | + Some example challenge. Blah blah blah flavor text. + + In case you missed it, this was written by {{ challenge.author }} + and is called {{ challenge.name }}. + + {{ nc }} # `nc somechal.chals.example.ctf 12345` + {{ link }} # [https://somechal.chals.example.ctf](https://somechal.chals.example.ctf) +``` + +## `category` + +- type: `string` +- default: from folder structure + +The category for the challenge, parsed from the directory structure. + +::: warning +This is automatically set from the expected directory structure of `//challenge.yaml` and should not be set in the file. +::: + +## `difficulty` + +- type: `integer` +- no default + +::: info +Not implemented yet, does nothing +::: + +The difficulty from the challenge, used to set point values. Values correspond to entries in the [rcds.yaml difficulty settings](rcds-yaml-reference#difficulty). + +```yaml +difficulty: 1 # the current default +``` + +## `flag`* + +- type: `string` | `dict` +- no default + +Where to find the flag for the challenge. The flag can be in a file, a regex, or a direct string. + +```yaml +# directly set +flag: ctf{example-flag} + +# from a file in in the challenge directory +flag: + file: ./flag + +# regex +flag: + regex: /ctf\{(foo|bar|ba[xyz])\}/ +``` + +::: info +Regex flags are not implemented yet and setting one does nothing +::: + +## `provide` + +- type: list of `string`/`dict` +- default: `[]` (no files) + +List of files to provide to the players on the frontend UI. These files can be from the challenge directory or from a container image built for a [challenge pod](#pods), and uploaded individually or zipped together. + +If there are no files to upload for this challenge, this can be omitted or set to an empty array. + +```yaml +provide: + # files from the challenge folder in the repo + - somefile.txt + - otherfile.txt + + # these are all equivalent + - foo.txt + - include: foo.txt + - include: [ foo.txt ] + + # rename a really long name as something shorter for upload + - as: short.h + include: some_really_long_name.h + + # multiple files from src/ in the challenge folder, zipped as together.zip + - as: together.zip + include: + - src/file1 + - src/file2 + - src/file3 + + # multiple files pulled from the container image for the `main` pod, + # uploaded individually as `notsh` and `libc.so.6` + - from: main + include: + - /chal/notsh + - /lib/x86_64-linux-gnu/libc.so.6 + + # single file pulled from the main container and renamed + - from: main + as: libc.so + include: /lib/x86_64-linux-gnu/libc.so.6 + + # multiple files pulled from the main container and zipped together + - from: main + as: notsh.zip + include: + - /chal/notsh + - /lib/x86_64-linux-gnu/libc.so.6 + + +# if no files need to be provided: +provide: [] +# or omit entirely +``` + +### `.include` + +- type: list of `string` +- no default + +File or list of files to upload individually, or include in a zip if `as` is set. + +When uploading, only the basename is used and the path to the file is discarded. + +If a provide item is specified as a single string, it is interpreted as an `include:`. + +### `.as` + +- type: `string` +- no default + +If `.include` is a single file, rename to this name while uploading. + +If multiple files, zip them together into the given zip file. + +### `.from` + +- type: `string` +- no default + +Fetch these files from the corresponding [challenge pod](#pods) image. + +## `pods` + +- type: list of `dict` +- default: `[]` (no pods) + +Defines how to build and deploy any services needed for the challenge. + +Challenge pods can be built from a local Dockerfile in the challenge folder or use an upstream image directly. + +If there are no pods or images needed for this challenge, this can be omitted or set to an empty array. + +```yaml +pods: + - name: main + build: . + ports: + - internal: 1337 # expose a container listening on port 1337 ... + expose: + http: examplechal # as a web chal at https://examplechal. + + - name: db + image: postgres:alpine + architecture: arm64 + env: + POSTGRES_USER: someuser + POSTGRES_PASSWORD: notsecure + +# if no containers or pods need to be deployed: +pods: [] +# or omit entirely +``` + +### `.name` + +- type: `string` +- no default + +Name of the pod, used to refer to this container as [a source for `provide` files](#provide) and for generated resource names. + +Cannot contain spaces or punctuation, only alphanumeric and `-`. + +### `.build` + +- type: `string` | `dict` +- no default + +Build the container image for this pod from a local `Dockerfile`. Supports a subset of the [docker-compose build spec](https://docs.docker.com/reference/compose-file/build/#illustrative-example), +either: + - a string path to the build context folder + - yaml with explicit `context` path, `dockerfile` path within context folder, + and `args` build args (only `context`, `dockerfile`, and `args` are + supported) + +The build context directory is relative to the `challenge.yaml`, and the `dockerfile` is relative to the context directory. + +Conflicts with [`image`](#image). + +```yaml + # build a container from a Dockerfile in the challenge folder + build: . + + # equivalent to the above but with explicit build context and Dockerfile name + build: + context: . + dockerfile: Dockerfile + + # build from a subfolder with a custom Dockerfile and some build args + build: + context: src/ + dockerfile: Containerfile.remote + args: + CC_OPTS: "-Osize" +``` + +### `.image` + +- type: `string` +- no default + +Use an available container image for the pod instead of building one from source. + +Conflicts with [`build`](#build). + +### `.env` + +- type: `dict` +- default: `{}` (no envvars) + +Any environment variables to set for the running pod. Specify as `name: value`. + +```yaml +env: + SOME_ENVVAR: foo bar +``` + +### `.architecture` + +- type: `string` +- default: `"amd64"` + +Set the desired CPU architecture to run this pod on. Kubernetes uses GOARCH architecture names. + +```yaml +architecture: amd64 +architecture: arm64 +``` + +### `.resources` + +- type: `dict` +- default: global default from `rcds.yaml` + +The CPU and memory resources that will be reserved for this pod. Kubernetes will make sure the requested amounts will be available for this pod to use, and will also restart the pod if it goes over these limits. + +Uses the [Kubernetes resource units](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#resource-units-in-kubernetes). + +If not set, the default set in [`rcds.yaml`](rcds-yaml-reference#resources) is used. + +```yaml +resources: + cpu: 1 + memory: 512Mi +``` + +### `.replicas` + +- type: `number` +- default: `2` + +How many instances of the pod to run. Traffic is load-balanced between instances. + +Default is 2 and this is probably fine unless the challenge is very resource intensive. + +```yaml +replicas: 2 # the default +``` + +### `.ports` + +- type: list of `dict` +- default: `[]` + +List of ports to expose to players. + +#### `.ports[].internal` + +- type: `number` +- no default + +The port that the challenge container (i.e. `xinetd`/`nginx`/etc inside) is listening on. + +#### `.ports[].expose` + +- type: `dict` +- no default + +How to expose the internal container port to players -- either as a TCP port or a subdirectory for web challenges. Must be one of the following: + +**`.ports[].expose.tcp`** + +- type: `number` +- no default + +The port to expose the challenge over raw TCP at on the challenge subdomain. Must be unique across all other exposed TCP challenges. + +```yaml [For TCP challenges] {8-10} +pods: + - #... + ports: + - internal: 31337 # the port the container listens on + expose: + tcp: 30124 # exposed at :30124 +``` + +**`.ports[].expose.http`** + +- type: `string` +- no default + +The subdomain to expose the challenge at as a website (port 80/443). This is prepended to the global challenge subdomain. The cluster will provision an SSL certificate for the site. + +Must be a valid DNS domain name (alphanumeric, `_`, `-`). + +```yaml [For web challenges] {8-10} +pods: + - name: main + build: + context: . + dockerfile: Dockerfile + replicas: 2 + ports: + - internal: 31337 # the port the container listens on + expose: + http: my-chal # exposed at https://my-chal. +``` + +### `.volume` + +- type: `string` +- no default + +::: info +Not implemented yet, does nothing +::: diff --git a/docs/reference/index.md b/docs/reference/index.md new file mode 100644 index 0000000..b983027 --- /dev/null +++ b/docs/reference/index.md @@ -0,0 +1,3 @@ +--- +title: Reference +--- diff --git a/docs/reference/rcds-yaml-reference.md b/docs/reference/rcds-yaml-reference.md new file mode 100644 index 0000000..85c2c5d --- /dev/null +++ b/docs/reference/rcds-yaml-reference.md @@ -0,0 +1,11 @@ +--- +title: rcds.yaml Reference +--- + +# `rcds.yaml` Config Reference + +`rcds.yaml` is the 'global' config for beaverCDS. This defines what challenges +are available, where to deploy them to, and what credentials to use for building +and deploying them. + +This will always be at `/rcds.yaml` in the challenges repository. diff --git a/docs/reference/resource-architecture.md b/docs/reference/resource-architecture.md new file mode 100644 index 0000000..33c689b --- /dev/null +++ b/docs/reference/resource-architecture.md @@ -0,0 +1 @@ +architecture of deployed resources diff --git a/src/builder/artifacts.rs b/src/builder/artifacts.rs index 1d6fc79..7f5b47b 100644 --- a/src/builder/artifacts.rs +++ b/src/builder/artifacts.rs @@ -41,6 +41,8 @@ pub async fn extract_asset( let extracted_files = match provide { // Repo file paths are relative to the challenge directory, so prepend chal dir + // No action necessary, return path as-is + ProvideConfig::FromRepoSingle { file } => Ok(vec![chal.directory.join(file)]), // No action necessary, return path as-is ProvideConfig::FromRepo { files } => { Ok(files.iter().map(|f| chal.directory.join(f)).collect_vec()) diff --git a/src/builder/docker.rs b/src/builder/docker.rs index e3fb4b4..f336974 100644 --- a/src/builder/docker.rs +++ b/src/builder/docker.rs @@ -39,7 +39,10 @@ pub async fn build_image( let client = docker().await?; let build_opts = BuildImageOptions { - dockerfile: options.dockerfile.clone(), + dockerfile: context + .join(&options.dockerfile) + .to_string_lossy() + .into_owned(), // coerce Cow str to String explicitly buildargs: options.args.clone(), t: tag.to_string(), forcerm: true, diff --git a/src/configparser/challenge.rs b/src/configparser/challenge.rs index 18c675d..c82d5e4 100644 --- a/src/configparser/challenge.rs +++ b/src/configparser/challenge.rs @@ -74,9 +74,11 @@ pub fn parse_one(path: &PathBuf) -> Result { .merge(Serialized::default("category", category)) .extract()?; - // coerce pod env lists to maps - // TODO: do this in serde deserialize? + let config = get_config()?; + for pod in parsed.pods.iter_mut() { + // coerce pod env lists to maps + // TODO: do this in serde deserialize? pod.env = match pod.env.clone() { ListOrMap::Map(m) => ListOrMap::Map(m), ListOrMap::List(l) => { @@ -104,6 +106,11 @@ pub fn parse_one(path: &PathBuf) -> Result { }); ListOrMap::Map(map) } + }; + + // set default resources from global config + if pod.resources.is_none() { + pod.resources = Some(config.defaults.resources.clone()) } } @@ -231,10 +238,19 @@ fn default_difficulty() -> i64 { #[fully_pub] enum FlagType { RawString(String), - File { file: PathBuf }, - Text { text: String }, - Regex { regex: String }, - Verifier { verifier: String }, + File { + file: PathBuf, + }, + String { + #[serde(alias = "text")] + string: String, + }, + Regex { + regex: String, + }, + Verifier { + verifier: String, + }, } // Parse each distinct kind of Provide action as a separate enum variant @@ -246,6 +262,10 @@ enum ProvideConfig { /// Upload file(s) as-is. /// Single or multiple files with no as: or from: /// Default if only a string is given. + FromRepoSingle { + #[serde(rename = "include")] + file: PathBuf, + }, FromRepo { #[serde(rename = "include")] files: Vec, @@ -299,8 +319,8 @@ enum ProvideConfig { impl FromStr for ProvideConfig { type Err = Void; fn from_str(s: &str) -> std::result::Result { - Ok(ProvideConfig::FromRepo { - files: vec![PathBuf::from(s)], + Ok(ProvideConfig::FromRepoSingle { + file: PathBuf::from(s), }) } } diff --git a/src/configparser/config.rs b/src/configparser/config.rs index 6527213..727c870 100644 --- a/src/configparser/config.rs +++ b/src/configparser/config.rs @@ -111,7 +111,7 @@ struct UserPass { pass: String, } -#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] #[fully_pub] struct Resource { cpu: i64, diff --git a/src/deploy/frontend.rs b/src/deploy/frontend.rs index 8e063e6..4a511fb 100644 --- a/src/deploy/frontend.rs +++ b/src/deploy/frontend.rs @@ -75,7 +75,7 @@ pub async fn update_frontend( .read_to_string(&mut flag); flag } - FlagType::Text { text } => text.clone(), + FlagType::String { string: text } => text.clone(), FlagType::Regex { regex } => unimplemented!(), FlagType::Verifier { verifier } => unimplemented!(), }; diff --git a/src/tests/parsing/challenges.rs b/src/tests/parsing/challenges.rs index 5e6a319..0da3bce 100644 --- a/src/tests/parsing/challenges.rs +++ b/src/tests/parsing/challenges.rs @@ -5,7 +5,28 @@ use std::path::PathBuf; #[cfg(test)] use pretty_assertions::{assert_eq, assert_ne}; -use crate::configparser::challenge::*; +use crate::configparser::{challenge::*, config::Resource}; + +const VALID_CONFIG: &str = r#" + flag_regex: ctf{[a-zA-Z_\-0-9]*} + + defaults: + difficulty: 1 + resources: { cpu: 1, memory: 200Mi } + + points: + - difficulty: 1 + min: 0 + max: 1337 + + registry: + domain: images.example.ctf + build: { user: "", pass: "" } + cluster: { user: "", pass: "" } + deploy: {} + profiles: {} + +"#; const VALID_CHAL: &str = r#" name: testchal @@ -24,6 +45,7 @@ const VALID_CHAL: &str = r#" /// No challenge files should parse correctly fn no_challenges() { figment::Jail::expect_with(|jail| { + jail.create_file("rcds.yaml", VALID_CONFIG)?; let chals = parse_all(); assert!(chals.is_ok()); @@ -37,6 +59,7 @@ fn no_challenges() { /// Challenge yaml at repo root should not parse fn challenge_in_root() { figment::Jail::expect_with(|jail| { + jail.create_file("rcds.yaml", VALID_CONFIG)?; jail.create_file("challenge.yaml", "name: test")?; let chals = parse_all(); @@ -52,6 +75,7 @@ fn challenge_in_root() { /// Challenge yaml one folder down should not parse fn challenge_one_level() { figment::Jail::expect_with(|jail| { + jail.create_file("rcds.yaml", VALID_CONFIG)?; let dir = jail.create_dir("foo")?; jail.create_file(dir.join("challenge.yaml"), "name: test")?; @@ -68,6 +92,7 @@ fn challenge_one_level() { /// Challenge yaml two folders down should be parsed fn challenge_two_levels() { figment::Jail::expect_with(|jail| { + jail.create_file("rcds.yaml", VALID_CONFIG)?; let dir = jail.create_dir("foo/test")?; jail.create_file(dir.join("challenge.yaml"), VALID_CHAL)?; @@ -88,8 +113,8 @@ fn challenge_two_levels() { category: "foo".to_string(), directory: PathBuf::from("foo/test"), - flag: FlagType::Text { - text: "test{it-works}".to_string() + flag: FlagType::String { + string: "test{it-works}".to_string() }, provide: vec![], @@ -105,6 +130,7 @@ fn challenge_two_levels() { /// Challenge yaml three folders down should not parsed fn challenge_three_levels() { figment::Jail::expect_with(|jail| { + jail.create_file("rcds.yaml", VALID_CONFIG)?; let dir = jail.create_dir("chals/foo/test")?; jail.create_file(dir.join("challenge.yaml"), VALID_CHAL)?; @@ -121,6 +147,7 @@ fn challenge_three_levels() { fn challenge_missing_fields() { figment::Jail::expect_with(|jail| { let dir = jail.create_dir("test/noflag")?; + jail.create_file("rcds.yaml", VALID_CONFIG)?; jail.create_file( dir.join("challenge.yaml"), r#" @@ -171,6 +198,7 @@ fn challenge_missing_fields() { /// Challenges can omit both provides and pods fields if needed fn challenge_no_provides_or_pods() { figment::Jail::expect_with(|jail| { + jail.create_file("rcds.yaml", VALID_CONFIG)?; let dir = jail.create_dir("foo/test")?; jail.create_file( dir.join("challenge.yaml"), @@ -198,6 +226,7 @@ fn challenge_no_provides_or_pods() { /// Challenge provide files parse correctly fn challenge_provide() { figment::Jail::expect_with(|jail| { + jail.create_file("rcds.yaml", VALID_CONFIG)?; let dir = jail.create_dir("foo/test")?; jail.create_file( dir.join("challenge.yaml"), @@ -213,6 +242,8 @@ fn challenge_provide() { provide: - foo.txt + - include: foo2.txt + - include: - bar.txt - baz.txt @@ -246,8 +277,11 @@ fn challenge_provide() { assert_eq!( chals[0].provide, vec![ - ProvideConfig::FromRepo { - files: vec!["foo.txt".into()] + ProvideConfig::FromRepoSingle { + file: "foo.txt".into() + }, + ProvideConfig::FromRepoSingle { + file: "foo2.txt".into() }, ProvideConfig::FromRepo { files: vec!["bar.txt".into(), "baz.txt".into()] @@ -285,6 +319,7 @@ fn challenge_provide() { /// Challenge provide files dont parse if include is missing from object form fn challenge_provide_no_include() { figment::Jail::expect_with(|jail| { + jail.create_file("rcds.yaml", VALID_CONFIG)?; let dir = jail.create_dir("foo/test")?; jail.create_file( dir.join("challenge.yaml"), @@ -315,6 +350,7 @@ fn challenge_provide_no_include() { /// Challenges should be able to have multiple pods fn challenge_pods() { figment::Jail::expect_with(|jail| { + jail.create_file("rcds.yaml", VALID_CONFIG)?; let dir = jail.create_dir("foo/test")?; jail.create_file( dir.join("challenge.yaml"), @@ -356,7 +392,10 @@ fn challenge_pods() { image_source: ImageSource::Image("nginx".to_string()), replicas: 2, env: ListOrMap::Map(HashMap::new()), - resources: None, + resources: Some(Resource { + cpu: 1, + memory: "200Mi".to_string() + }), architecture: "amd64".to_string(), ports: vec![PortConfig { internal: 80, @@ -373,7 +412,10 @@ fn challenge_pods() { }), replicas: 1, env: ListOrMap::Map(HashMap::new()), - resources: None, + resources: Some(Resource { + cpu: 1, + memory: "200Mi".to_string() + }), architecture: "amd64".to_string(), ports: vec![PortConfig { internal: 8000, @@ -392,6 +434,7 @@ fn challenge_pods() { /// Challenge pods can use simple or complex build options fn challenge_pod_build() { figment::Jail::expect_with(|jail| { + jail.create_file("rcds.yaml", VALID_CONFIG)?; let dir = jail.create_dir("foo/test")?; jail.create_file( dir.join("challenge.yaml"), @@ -443,7 +486,10 @@ fn challenge_pod_build() { }), replicas: 1, env: ListOrMap::Map(HashMap::new()), - resources: None, + resources: Some(Resource { + cpu: 1, + memory: "200Mi".to_string() + }), architecture: "amd64".to_string(), ports: vec![PortConfig { internal: 80, @@ -463,7 +509,10 @@ fn challenge_pod_build() { }), replicas: 1, env: ListOrMap::Map(HashMap::new()), - resources: None, + resources: Some(Resource { + cpu: 1, + memory: "200Mi".to_string() + }), architecture: "amd64".to_string(), ports: vec![PortConfig { internal: 80, @@ -482,6 +531,7 @@ fn challenge_pod_build() { /// Challenge pod envvars can be set as either string list or map fn challenge_pod_env() { figment::Jail::expect_with(|jail| { + jail.create_file("rcds.yaml", VALID_CONFIG)?; let dir = jail.create_dir("foo/test")?; jail.create_file( dir.join("challenge.yaml"), @@ -533,7 +583,10 @@ fn challenge_pod_env() { ("FOO".to_string(), "this".to_string()), ("BAR".to_string(), "that".to_string()), ])), - resources: None, + resources: Some(Resource { + cpu: 1, + memory: "200Mi".to_string() + }), architecture: "amd64".to_string(), ports: vec![PortConfig { internal: 80, @@ -549,7 +602,10 @@ fn challenge_pod_env() { ("FOO".to_string(), "this".to_string()), ("BAR".to_string(), "that".to_string()), ])), - resources: None, + resources: Some(Resource { + cpu: 1, + memory: "200Mi".to_string() + }), architecture: "amd64".to_string(), ports: vec![PortConfig { internal: 80, @@ -568,6 +624,7 @@ fn challenge_pod_env() { /// Challenge pod envvar strings error if malformed fn challenge_pod_bad_env() { figment::Jail::expect_with(|jail| { + jail.create_file("rcds.yaml", VALID_CONFIG)?; let dir = jail.create_dir("foo/test")?; jail.create_file( dir.join("challenge.yaml"), @@ -601,3 +658,61 @@ fn challenge_pod_bad_env() { Ok(()) }) } + +#[test] +/// Challenge pod resources override config defaults +fn challenge_pod_resources() { + figment::Jail::expect_with(|jail| { + jail.create_file("rcds.yaml", VALID_CONFIG)?; + let dir = jail.create_dir("foo/test")?; + jail.create_file( + dir.join("challenge.yaml"), + r#" + name: testchal + author: nobody + description: just a test challenge + difficulty: 1 + + flag: + text: test{it-works} + + pods: + - name: foo + image: nginx + replicas: 1 + resources: + cpu: 4 + memory: 1Gi + ports: + - internal: 80 + expose: + http: test.chals.example.com + "#, + )?; + + let chals = parse_all().unwrap(); + + assert_eq!( + chals[0].pods, + vec![Pod { + name: "foo".to_string(), + + image_source: ImageSource::Image("nginx".to_string()), + replicas: 1, + env: ListOrMap::Map(HashMap::new()), + resources: Some(Resource { + cpu: 4, + memory: "1Gi".to_string() + }), + architecture: "amd64".to_string(), + ports: vec![PortConfig { + internal: 80, + expose: ExposeType::Http("test.chals.example.com".to_string()) + }], + volume: None + }] + ); + + Ok(()) + }) +}