From f539058985a56f0c4de7a35fcb8fbf1c443b675f Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 25 Oct 2026 15:09:00 +0000 Subject: [PATCH 1/2] docs: start the developer docs site with self-hosting and the API Starlight on Bun, deployed to Cloudflare like chek.page, with Chek's brand tokens and self-hosted fonts. Covers getting started, running Chek server and the API, with the reference built from openapi.yaml at chek 0.3.0. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01PY1z7gbTSbKghKSF1tWxKN --- .github/pull_request_template.md | 10 + .github/workflows/ci.yml | 30 + .gitignore | 22 + .oxfmtrc.json | 5 + .oxlintrc.json | 7 + AGENTS.md | 49 + CLAUDE.md | 1 + README.md | 23 +- astro.config.ts | 61 + bun.lock | 1152 +++++++++++++++++ cloudflare.config.ts | 10 + package.json | 31 + public/favicon.ico | Bin 0 -> 14510 bytes public/favicon.svg | 1 + public/fonts/OFL-BricolageGrotesque.txt | 93 ++ public/fonts/OFL-DMSans.txt | 93 ++ public/fonts/bricolage-700-593306227576.woff2 | Bin 0 -> 34348 bytes public/fonts/dm-sans-400-2ea1bf6e4127.woff2 | Bin 0 -> 19164 bytes public/fonts/dm-sans-600-fd6f63245e24.woff2 | Bin 0 -> 19192 bytes scripts/cloudflare.ts | 27 + src/assets/mark.svg | 1 + src/content.config.ts | 7 + src/content/docs/api/overview.md | 73 ++ src/content/docs/api/reading.md | 88 ++ src/content/docs/get-started/introduction.md | 24 + src/content/docs/get-started/quick-start.md | 74 ++ src/content/docs/index.mdx | 35 + .../docs/self-hosting/configuration.md | 62 + src/content/docs/self-hosting/extensions.md | 68 + src/content/docs/self-hosting/first-steps.md | 62 + src/content/docs/self-hosting/install.md | 94 ++ .../docs/self-hosting/komga-and-kavita.md | 60 + src/content/docs/self-hosting/logs.md | 19 + src/content/docs/self-hosting/opds.md | 21 + .../docs/self-hosting/reverse-proxy.md | 71 + .../docs/self-hosting/sign-in-providers.md | 60 + .../docs/self-hosting/troubleshooting.md | 45 + .../docs/self-hosting/updating-and-backups.md | 51 + src/content/docs/self-hosting/uploads.md | 21 + src/content/docs/self-hosting/users.md | 42 + src/styles/brand.css | 50 + tsconfig.json | 8 + 42 files changed, 2649 insertions(+), 2 deletions(-) create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 .oxfmtrc.json create mode 100644 .oxlintrc.json create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 astro.config.ts create mode 100644 bun.lock create mode 100644 cloudflare.config.ts create mode 100644 package.json create mode 100644 public/favicon.ico create mode 100644 public/favicon.svg create mode 100644 public/fonts/OFL-BricolageGrotesque.txt create mode 100644 public/fonts/OFL-DMSans.txt create mode 100644 public/fonts/bricolage-700-593306227576.woff2 create mode 100644 public/fonts/dm-sans-400-2ea1bf6e4127.woff2 create mode 100644 public/fonts/dm-sans-600-fd6f63245e24.woff2 create mode 100644 scripts/cloudflare.ts create mode 100644 src/assets/mark.svg create mode 100644 src/content.config.ts create mode 100644 src/content/docs/api/overview.md create mode 100644 src/content/docs/api/reading.md create mode 100644 src/content/docs/get-started/introduction.md create mode 100644 src/content/docs/get-started/quick-start.md create mode 100644 src/content/docs/index.mdx create mode 100644 src/content/docs/self-hosting/configuration.md create mode 100644 src/content/docs/self-hosting/extensions.md create mode 100644 src/content/docs/self-hosting/first-steps.md create mode 100644 src/content/docs/self-hosting/install.md create mode 100644 src/content/docs/self-hosting/komga-and-kavita.md create mode 100644 src/content/docs/self-hosting/logs.md create mode 100644 src/content/docs/self-hosting/opds.md create mode 100644 src/content/docs/self-hosting/reverse-proxy.md create mode 100644 src/content/docs/self-hosting/sign-in-providers.md create mode 100644 src/content/docs/self-hosting/troubleshooting.md create mode 100644 src/content/docs/self-hosting/updating-and-backups.md create mode 100644 src/content/docs/self-hosting/uploads.md create mode 100644 src/content/docs/self-hosting/users.md create mode 100644 src/styles/brand.css create mode 100644 tsconfig.json diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..f1a5708 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,10 @@ +## Before + +## After + +## How + +## Checks + +- [ ] `bun run check`, `bun run lint`, `bun run format:check` and `bun run build` pass +- [ ] Pages describe what the linked release does, not what's planned diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..04f900d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +name: CI + +# Pushes to other branches are covered by their pull request, so running on +# both would check every commit twice. +on: + push: + branches: [main] + pull_request: + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + site: + name: Lint, check and build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: oven-sh/setup-bun@v2 + with: + bun-version-file: package.json + - run: bun install --frozen-lockfile + - run: bun run lint + - run: bun run format:check + - run: bun run check + - run: bun run build diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e339aaa --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +node_modules/ +dist/ +.astro/ +out/ +coverage/ +*.tsbuildinfo +*.log +.env +.env.* +!.env.example +.DS_Store + +# Share editor settings even when .vscode is globally ignored. +!.vscode/ +.vscode/* +!.vscode/settings.json +!.vscode/extensions.json + +# Cloudflare deployment artifacts +.cloudflare/ + +.agents/ diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 0000000..0545800 --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,5 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "semi": false, + "ignorePatterns": ["AGENTS.md", "bun.lock", "**/dist/**", "**/.astro/**", "src/content/**"] +} diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..ffc2d32 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,7 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "categories": { + "correctness": "error" + }, + "ignorePatterns": ["**/node_modules/**", "**/dist/**", "**/out/**", "**/coverage/**"] +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..273c4af --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,49 @@ +# AGENTS.md + +Guidance for anyone changing this repository, human or agent. Read it before writing. + +## What this is + +The public developer documentation for Chek, served at docs.chek.page. It's for people who self-host Chek server, build on its API, write source extensions, or embed Chek Reader. Product direction lives in [readchek/chek's docs/product.md](https://github.com/readchek/chek/blob/main/docs/product.md); the marketing site is [readchek/chek.page](https://github.com/readchek/chek.page). + +## Decisions already made + +- **Astro Starlight**, built with Bun and deployed to Cloudflare as static files, like chek.page. Pages are Markdown or MDX in `src/content/docs`. Starlight ships no client framework; if a page ever needs an interactive component, write it in SolidJS through `@astrojs/solid-js`, never React or Vue. +- **Sources of truth stay in their repositories.** The API reference is built by `starlight-openapi` from `openapi.yaml` at the chek release named by `chekRelease` in `astro.config.ts`. Don't copy the spec here. Raise `chekRelease` when the guides have caught up with a new release. +- **Describe what's released.** Pages describe what the release in `chekRelease` does, not what's planned or only on `main`. +- **Public only.** Nothing here names private infrastructure, private repositories or anyone's credentials. Design decisions and internal deploys stay in their own repositories' `docs/`. +- **Brand.** Colors and fonts come from chek.page's tokens, set as Starlight variables in `src/styles/brand.css`. Fonts are served from the site, so the page never calls another origin. + +## Layout + +| Path | Contents | +| --- | --- | +| `src/content/docs/get-started` | What Chek is and the quick start | +| `src/content/docs/self-hosting` | Installing, configuring and running Chek server | +| `src/content/docs/api` | API guides. The reference under `/api/reference` is generated | +| `astro.config.ts` | Site config, sidebar and the pinned chek release | +| `scripts/cloudflare.ts` | Turns the static build into Cloudflare's prebuilt output | + +## Build + +```sh +bun install +bun run dev # http://localhost:4321 +bun run lint && bun run format:check && bun run check && bun run build +``` + +The build fetches `openapi.yaml` from GitHub, so it needs network. `bun run deploy` builds and publishes to Cloudflare, after `cf auth login`. + +## Writing + +- Write for someone who has never seen Chek: what to do, in order, with commands that work when pasted. Explain why only where it changes what they do. +- Check every setting, endpoint and default against the code or `openapi.yaml` at `chekRelease` before writing it down. +- Short sentences, active voice, second person. Sentence case for titles. Be exact: real variable names, real error codes. +- Every page has a `description` in its front matter, used for search and link previews. +- Link between pages with root-relative paths ending in `/`, such as `/self-hosting/install/`. + +## Pull requests + +- Branch from `main`, keep each PR to one change, and merge by squash. +- Fill in the PR template with "Before", "After" and "How" in plain language. +- When a change in chek, chek-reader or app alters something these pages describe, open a PR here too. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/README.md b/README.md index 86ba594..f6ec6b4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,22 @@ -# Chek Docs +

+ Chek +

-Developer documentation for Chek, served at [docs.chek.page](https://docs.chek.page). +

Chek Docs

+ +

Developer documentation for Chek, at docs.chek.page.

+ +Guides for running your own Chek server, building on its API, writing source extensions and embedding Chek Reader, with the API reference built from the server's `openapi.yaml`. + +## Run it + +```sh +bun install +bun run dev # http://localhost:4321 +``` + +Before a pull request, run `bun run lint`, `bun run format:check`, `bun run check` and `bun run build`; CI runs them too. [AGENTS.md](AGENTS.md) covers how the site is built and how to write for it. + +## Deploy + +`bun run deploy` builds the site and publishes it to Cloudflare at docs.chek.page, after `cf auth login`. diff --git a/astro.config.ts b/astro.config.ts new file mode 100644 index 0000000..bdfcc86 --- /dev/null +++ b/astro.config.ts @@ -0,0 +1,61 @@ +import starlight from "@astrojs/starlight" +import { defineConfig } from "astro/config" +import starlightOpenAPI, { openAPISidebarGroups } from "starlight-openapi" + +// The server release these pages describe. The API reference is built from +// that release's openapi.yaml, so raise this when the guides catch up with a +// new release, not before. +const chekRelease = "0.3.0" + +export default defineConfig({ + site: "https://docs.chek.page", + integrations: [ + starlight({ + title: "Chek Docs", + description: + "Run your own Chek server, build on its API, write source extensions and embed Chek Reader.", + logo: { src: "./src/assets/mark.svg", alt: "Chek" }, + favicon: "/favicon.svg", + customCss: ["./src/styles/brand.css"], + social: [{ icon: "github", label: "GitHub", href: "https://github.com/readchek" }], + editLink: { baseUrl: "https://github.com/readchek/docs/edit/main/" }, + plugins: [ + starlightOpenAPI([ + { + base: "api/reference", + schema: `https://raw.githubusercontent.com/readchek/chek/${chekRelease}/openapi.yaml`, + sidebar: { label: "Reference", operations: { badges: true } }, + }, + ]), + ], + sidebar: [ + { + label: "Get started", + items: ["get-started/introduction", "get-started/quick-start"], + }, + { + label: "Self-hosting", + items: [ + "self-hosting/install", + "self-hosting/first-steps", + "self-hosting/configuration", + "self-hosting/reverse-proxy", + "self-hosting/users", + "self-hosting/sign-in-providers", + "self-hosting/komga-and-kavita", + "self-hosting/opds", + "self-hosting/uploads", + "self-hosting/extensions", + "self-hosting/updating-and-backups", + "self-hosting/logs", + "self-hosting/troubleshooting", + ], + }, + { + label: "API", + items: ["api/overview", "api/reading", ...openAPISidebarGroups], + }, + ], + }), + ], +}) diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..96be819 --- /dev/null +++ b/bun.lock @@ -0,0 +1,1152 @@ +{ + "lockfileVersion": 2, + "configVersion": 1, + "workspaces": { + "": { + "name": "chek-docs", + "dependencies": { + "@astrojs/markdown-satteri": "0.4.5", + "@astrojs/starlight": "0.42.7", + "astro": "7.4.2", + "sharp": "0.35.5", + "starlight-openapi": "0.27.0", + }, + "devDependencies": { + "@astrojs/check": "0.9.12", + "@cloudflare/build-output-utils": "0.9.3", + "@types/bun": "^1.4.4", + "cf": "^1.0.0-beta.13", + "oxfmt": "^0.73.0", + "oxlint": "^1.86.0", + "typescript": "^6", + }, + }, + }, + "packages": { + "@apidevtools/json-schema-ref-parser": ["@apidevtools/json-schema-ref-parser@13.0.5", "", { "dependencies": { "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0" } }, "sha512-xfh4xVJD62gG6spIc7lwxoWT+l16nZu1ELyU8FkjaP/oD2yP09EvLAU6KhtudN9aML2Khhs9pY6Slr7KGTES3w=="], + + "@astrojs/astro2tsx": ["@astrojs/astro2tsx@0.1.3", "", { "dependencies": { "@emnapi/core": "1.11.3", "@napi-rs/wasm-runtime": "1.2.4" } }, "sha512-GMDC7Eo0myBUA/QLBl0u+OcegJ8LiBhccmNZR34KmprQNP8gQUJlWlOOZWUsxNA2rNS6GuPYcGDyLoPnjmn4/Q=="], + + "@astrojs/check": ["@astrojs/check@0.9.12", "", { "dependencies": { "@astrojs/language-server": "^2.17.3", "chokidar": "^5.0.0", "kleur": "^4.1.5", "yargs": "^18.2.0" }, "peerDependencies": { "typescript": "^5.0.0 || ^6.0.0" }, "bin": { "astro-check": "./bin/astro-check.js" } }, "sha512-hazpqxIfZWj2VhyPyR0pOEQ/IhFDM/2nIApL96zMXABdD8NrUKYDmhxTy7h+drTDl9MQG/Sd1ET/4amadVJUUA=="], + + "@astrojs/compiler-binding": ["@astrojs/compiler-binding@0.5.2", "", { "optionalDependencies": { "@astrojs/compiler-binding-android-arm64": "0.5.2", "@astrojs/compiler-binding-darwin-arm64": "0.5.2", "@astrojs/compiler-binding-darwin-x64": "0.5.2", "@astrojs/compiler-binding-linux-arm64-gnu": "0.5.2", "@astrojs/compiler-binding-linux-arm64-musl": "0.5.2", "@astrojs/compiler-binding-linux-x64-gnu": "0.5.2", "@astrojs/compiler-binding-linux-x64-musl": "0.5.2", "@astrojs/compiler-binding-wasm32-wasi": "0.5.2", "@astrojs/compiler-binding-win32-arm64-msvc": "0.5.2", "@astrojs/compiler-binding-win32-x64-msvc": "0.5.2" } }, "sha512-lEn76+0JcMQ5RDXU0wSMWJg2ZppiZvSULpghC29cfv4o9bhvWFDbQjsCtvjjXN9iVLFQ7fL3wTR2L2UU13mP8g=="], + + "@astrojs/compiler-binding-android-arm64": ["@astrojs/compiler-binding-android-arm64@0.5.2", "", { "os": "android", "cpu": "arm64" }, "sha512-kckIxon6Rm/Bxzh9mGPYpRRqUbToUQoHptCmpMmE+Yt0Xumq8CqrKUidyYhrBsrHVO7bxuRzzWUUqvpf+iYceQ=="], + + "@astrojs/compiler-binding-darwin-arm64": ["@astrojs/compiler-binding-darwin-arm64@0.5.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1ZG5Ar3dD8HtodX9XH1mRmE0qBXZlvjYXSFfpgFwocqfWnvhFuqtGUNseL8wgfImiRMztIjpQqlogQFQQtgiag=="], + + "@astrojs/compiler-binding-darwin-x64": ["@astrojs/compiler-binding-darwin-x64@0.5.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-+kl35S7hiHJ2cb7eujwEsPFYYuSgE11uEE2Z20lAJ+undpgulMcW+weQqFPOSSZccEKHOiLIY+kQHkb4e4092w=="], + + "@astrojs/compiler-binding-linux-arm64-gnu": ["@astrojs/compiler-binding-linux-arm64-gnu@0.5.2", "", { "os": "linux", "cpu": "arm64", "libc": "glibc" }, "sha512-BqlR0EHNQLPLwgL9+xogjsAc7zGf0cHho2CPdhv0P1yqbVMwGVaGAqrANP6VYAXCqSC43AF4Czcxc2n+oqdesg=="], + + "@astrojs/compiler-binding-linux-arm64-musl": ["@astrojs/compiler-binding-linux-arm64-musl@0.5.2", "", { "os": "linux", "cpu": "arm64", "libc": "musl" }, "sha512-GRGyby3AZWw5d6JQzidyxPyvGgHWsSusrpgKyFVpDTKctABF7L3Pd5V1cRJbV36/rcu+Eh292/wL7pF7MkmGkQ=="], + + "@astrojs/compiler-binding-linux-x64-gnu": ["@astrojs/compiler-binding-linux-x64-gnu@0.5.2", "", { "os": "linux", "cpu": "x64", "libc": "glibc" }, "sha512-9XAqGTMQAtIslMsjK3xdyY6OI9d59OZqcLBB6pvD/DcoR0daYswfrKx2azdDERDY4AqAnWPC7odVM1BmxYv3Vw=="], + + "@astrojs/compiler-binding-linux-x64-musl": ["@astrojs/compiler-binding-linux-x64-musl@0.5.2", "", { "os": "linux", "cpu": "x64", "libc": "musl" }, "sha512-jOViewGeTh9bSfS8vK72ECxoy2yvmA+Pd/ZcEvBI5c7+7IcfHwZ5qA4L9fRYuOhSrAifvQ2dV4vm1XAtloaWBw=="], + + "@astrojs/compiler-binding-wasm32-wasi": ["@astrojs/compiler-binding-wasm32-wasi@0.5.2", "", { "dependencies": { "@emnapi/core": "1.11.3", "@emnapi/runtime": "1.11.3", "@napi-rs/wasm-runtime": "^1.2.5" }, "cpu": "none" }, "sha512-Z7p64doar1uvR5C9Xs6j1CV9lGlPVQX7OQ2aAtZ/xKC5aGS6GB13nyc2UfQXTQLvZrvVwsyaflTQy6ziY5Bekw=="], + + "@astrojs/compiler-binding-win32-arm64-msvc": ["@astrojs/compiler-binding-win32-arm64-msvc@0.5.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Tr13bh+ahTWsjXjzctK7pFpWV+dUU4FtBfETlR7gO+ueex0F+RDch7eRr0YfKysaRhk9tWm14lsCJ5uWyFPNHQ=="], + + "@astrojs/compiler-binding-win32-x64-msvc": ["@astrojs/compiler-binding-win32-x64-msvc@0.5.2", "", { "os": "win32", "cpu": "x64" }, "sha512-SfKkI+jOQYLh6wojPvVPV0lFfQnxisweN9B+qCzRvZp7DHAuGif8Y0WcC5PUgN4jb8VKy57LggQDtggR46Du1g=="], + + "@astrojs/compiler-rs": ["@astrojs/compiler-rs@0.5.2", "", { "dependencies": { "@astrojs/compiler-binding": "0.5.2" } }, "sha512-IvwYLB8t1M86rKO4oPdBi9GP4gzXcgrVlNgocjwos1jeeUuEIV8d9Crn1PvYD/5IKteWOxpIHomf7e8iYlkE+Q=="], + + "@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.13.0", "", { "dependencies": { "@types/hast": "^3.0.5", "@types/mdast": "^4.0.4", "picomatch": "^4.0.7", "retext-smartypants": "^6.2.0", "shiki": "^4.5.0", "smol-toml": "^1.9.1", "unified": "^11.0.5", "yaml": "^2.9.1" } }, "sha512-20jxQqnSmpskcGyad7N5l6Xf1gz0UCa6lNde44bEBdaXGpncEYfAZJ142r2ZgzBaG4dUyzGmhqZWv1INU2eiHQ=="], + + "@astrojs/language-server": ["@astrojs/language-server@2.17.3", "", { "dependencies": { "@astrojs/astro2tsx": "^0.1.2", "@astrojs/yaml2ts": "^0.2.4", "@volar/kit": "~2.4.28", "@volar/language-core": "~2.4.28", "@volar/language-server": "~2.4.28", "@volar/language-service": "~2.4.28", "muggle-string": "^0.4.1", "tinyglobby": "^0.2.17", "volar-service-css": "0.0.71", "volar-service-emmet": "0.0.71", "volar-service-html": "0.0.71", "volar-service-prettier": "0.0.71", "volar-service-typescript": "0.0.71", "volar-service-typescript-twoslash-queries": "0.0.71", "volar-service-yaml": "0.0.71", "vscode-html-languageservice": "^5.6.2", "vscode-uri": "^3.2.0" }, "peerDependencies": { "prettier": "^3.0.0", "prettier-plugin-astro": ">=0.11.0" }, "optionalPeers": ["prettier", "prettier-plugin-astro"], "bin": { "astro-ls": "./bin/nodeServer.js" } }, "sha512-QIAFu1e/4quJMTPdmBOm+ZPXc5NDGZE4/YQjgR3EgjhGLl/tdidaWJDr5WmO2lesVc+BW1qN/xNBURmuePDG2A=="], + + "@astrojs/markdown-satteri": ["@astrojs/markdown-satteri@0.4.5", "", { "dependencies": { "@astrojs/internal-helpers": "0.13.0", "@astrojs/prism": "4.1.1", "github-slugger": "^2.0.0", "satteri": "^0.10.5" } }, "sha512-0IeGdoFbv0f+442KJhYS44Wg8F3MuHmRsxIEYuFyqkjRubc6dPBFZ7azor5t4AeXLQoT5A5h9HyViKpd70Dl9w=="], + + "@astrojs/mdx": ["@astrojs/mdx@8.1.1", "", { "dependencies": { "@astrojs/internal-helpers": "0.13.0", "@astrojs/markdown-satteri": "0.4.5", "es-module-lexer": "^3.0.3" }, "peerDependencies": { "@astrojs/markdown-remark": "^7.3.0", "astro": "^7.2.10" }, "optionalPeers": ["@astrojs/markdown-remark"] }, "sha512-jEJUJsO2KqmKfBXaKeDHDCCj+Kerj1Fj+XndHznF9wxahvs+1onYCBeKmieM28N2+uA6mW57DOQQw5YaDLdBnA=="], + + "@astrojs/prism": ["@astrojs/prism@4.1.1", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-sXlAXJX6t4QfuLg+CuCDzpg7NoxWcyb4paFprgtFppLeisCBmh935h+tJ5CWXfsXEoW2IDy/ihbRMtiu28Chjw=="], + + "@astrojs/sitemap": ["@astrojs/sitemap@3.7.4", "", { "dependencies": { "sitemap": "^9.0.0", "zod": "^4.3.6" } }, "sha512-LbKNC24bdUWcQf/pThB6qLlSqHojxGjZDURIzFocY8rlWnAn2t74nnhnK6S5x0NHriHoAduLEpVjRykmeGiVvA=="], + + "@astrojs/starlight": ["@astrojs/starlight@0.42.7", "", { "dependencies": { "@astrojs/markdown-satteri": "^0.4.0", "@astrojs/mdx": "^8.0.0", "@astrojs/sitemap": "^3.7.3", "@pagefind/default-ui": "^1.3.0", "@types/hast": "^3.0.5", "@types/js-yaml": "^4.0.9", "@types/mdast": "^4.0.4", "astro-expressive-code": "^0.44.0", "bcp-47": "^2.1.0", "hast-util-format": "^1.1.0", "hast-util-select": "^6.0.4", "hast-util-to-html": "^9.0.5", "hast-util-to-string": "^3.0.1", "hastscript": "^9.0.1", "i18next": "^26.0.7", "js-yaml": "^4.1.1", "klona": "^2.0.6", "magic-string": "^1.2.3", "mdast-util-directive": "^3.1.0", "mdast-util-to-markdown": "^2.1.2", "mdast-util-to-string": "^4.0.0", "pagefind": "^1.5.2", "remark-directive": "^4.0.0", "satteri": "^0.10.3", "ultrahtml": "^1.6.0", "unified": "^11.0.5", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3" }, "peerDependencies": { "@astrojs/markdown-remark": "^7.3.0", "astro": "^7.2.10" }, "optionalPeers": ["@astrojs/markdown-remark"] }, "sha512-mlbit5iRjLx5eIcLf84Q2CwkrrsEgZrQwKKBK+vRf+ZGiSrwy1UGdT1hUfAIaHrMfcvkDpL+gw3aua7HobiKFg=="], + + "@astrojs/telemetry": ["@astrojs/telemetry@3.4.1", "", { "dependencies": { "ci-info": "^4.4.0", "dset": "^3.1.4", "is-docker": "^4.0.0", "package-manager-detector": "^1.9.0" } }, "sha512-SMkvMsGOpM0FMAoT5pzhgI3LZC0JSPWf7Evwop+fZ3N6UkusPM7lI+sOJjMNI5OJrVoiei1/AGo1cY7sikTc5A=="], + + "@astrojs/yaml2ts": ["@astrojs/yaml2ts@0.2.4", "", { "dependencies": { "yaml": "^2.8.3" } }, "sha512-8oddpOae35pJsXPQXhTkM0ypfKPskVsh2bCxRtbf7e+/Epw2nReakFYpLKjZMEr75CsoF203PMnCocpfz0s69A=="], + + "@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.11", "", {}, "sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/parser": ["@babel/parser@7.29.11", "", { "dependencies": { "@babel/types": "^7.29.11" }, "bin": "./bin/babel-parser.js" }, "sha512-OZ6+oaK7GMyg3IfCpj8JOmn8n+w2rJbDT0LLfVDTLb1ZFeKDh3AiNELgCMUw175IJHSkLl74hG0M0VLsvx+gag=="], + + "@babel/runtime": ["@babel/runtime@7.29.10", "", {}, "sha512-NpugTzbKgGrNe45Ui32cN8vCFFMxVtP/9rtfwv0nP5FRDAjvYBxJcLVpYA9mr1jxP6cnIT7x9LUzuDTbWYYNRA=="], + + "@babel/types": ["@babel/types@7.29.11", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.11", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ=="], + + "@bruits/satteri-darwin-arm64": ["@bruits/satteri-darwin-arm64@0.10.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-27KTVl4TJkVahMy/ohyA7qd4938G5UNneFUz/PsScYfpIhj0IVAS23mpcJXdPF44sa6nva198lmV/cKIb2YPyA=="], + + "@bruits/satteri-darwin-x64": ["@bruits/satteri-darwin-x64@0.10.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-IjnLe3nKspq6qaeqGgjT7MT8VrTV74yWRlaag7ZdNsI8TDAYZ0iPxMCo+9KQZHUk5EyVB+reBI/PFWL5KuFw9Q=="], + + "@bruits/satteri-linux-arm64-gnu": ["@bruits/satteri-linux-arm64-gnu@0.10.5", "", { "os": "linux", "cpu": "arm64", "libc": "glibc" }, "sha512-glkYXZCJywjP13v67eAyAMSJdF+ncvEbYvgi/wOtffL9tQ27lr/zsyzUfgs+ovjJ9d8JNQKiXeiArJcX8PJL9w=="], + + "@bruits/satteri-linux-arm64-musl": ["@bruits/satteri-linux-arm64-musl@0.10.5", "", { "os": "linux", "cpu": "arm64", "libc": "musl" }, "sha512-yWdgG1g17Nh2QyGVlFUxGRa3FEFwiMcpZEyMNWkbM3deC94cmVc+/i9OuyFpdKuWo3GkgoCtYVOoxk1uCnCZIA=="], + + "@bruits/satteri-linux-x64-gnu": ["@bruits/satteri-linux-x64-gnu@0.10.5", "", { "os": "linux", "cpu": "x64", "libc": "glibc" }, "sha512-FVaLoPT1fBgGl0J+AYebyyXJYBachGl8Oyyrf1lye4RTqCB4S0Gwkj1uM9RJyThUOvx5VUmAT1CnNh1SFHA+kw=="], + + "@bruits/satteri-linux-x64-musl": ["@bruits/satteri-linux-x64-musl@0.10.5", "", { "os": "linux", "cpu": "x64", "libc": "musl" }, "sha512-EHpVAx2bqW3GINHTKkljtxVfQmVDGWIuwOYOP5YghTj+0PkBa2o8oKPRtQ9Kbsr1Fye8jtUcDjhwj2jMNugZKg=="], + + "@bruits/satteri-wasm32-wasi": ["@bruits/satteri-wasm32-wasi@0.10.5", "", { "dependencies": { "@emnapi/core": "1.11.1", "@emnapi/runtime": "1.11.1", "@napi-rs/wasm-runtime": "^1.2.3" }, "cpu": "none" }, "sha512-ypz8c/Zmipxp4IoeDa228Gstv6TLzVmNs3yC6wKCoNSOjx1iwpgzu87Y3hTkXFdwChVGU85qeUDuOIarGUZQLw=="], + + "@bruits/satteri-win32-arm64-msvc": ["@bruits/satteri-win32-arm64-msvc@0.10.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-siTV88nb0LRqNpkL2gXboqCwVdq95sLtzMHS1/3eONV2gLbB3NAK46wmSMvCO/yquBvI2lvaFIfd8P12ecsxBw=="], + + "@bruits/satteri-win32-x64-msvc": ["@bruits/satteri-win32-x64-msvc@0.10.5", "", { "os": "win32", "cpu": "x64" }, "sha512-C3IfPvfvMXmlzBxaMPKFS1XiuV9pu2mC7YqkPk7PSvTgPZ8gbdASIpHpztDLvTTQjqZ0z1Ol8tK5X+V6XXC0wQ=="], + + "@capsizecss/unpack": ["@capsizecss/unpack@4.0.1", "", { "dependencies": { "fontkitten": "^1.0.3" } }, "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ=="], + + "@clack/core": ["@clack/core@1.5.1", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-iHTrHA8MtVuLl2TfZySmcKv1qO2PoyC9Z7pfSDozEuV5vtY3/wcOPKJXlqJ5Oq2Cx5DDGQGAMVx6HZfRRoVEbQ=="], + + "@clack/prompts": ["@clack/prompts@1.8.1", "", { "dependencies": { "@clack/core": "1.5.1", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-dlT1m5e/0yUL0kRNcQn7yGLVThkgbB0Ga/1AmfDDC/8ik6AIiSf2QLQO2zPYvefsHP0aFgxO93cVLCCfDp7kzQ=="], + + "@cloudflare/build-output-utils": ["@cloudflare/build-output-utils@0.9.3", "", { "dependencies": { "@cloudflare/config": "0.27.0" } }, "sha512-0MgvIq6UxbRWLwB2QJU7qjLqpPkfygOebN3UiENhdp+z6zpRKrNnwkms2ynadmxCOh3sL2v+SI0/pNXxgXdteA=="], + + "@cloudflare/config": ["@cloudflare/config@0.27.0", "", { "dependencies": { "zod": "4.6.5" } }, "sha512-wNnL0z4lj5Gm2fze5qXTOvRPbUOi4jlMmwPk7ArCUakI5oVxgjfl3Rhnfd/Mmzo3g86x/ofH75yPIhsa5msqdQ=="], + + "@cloudflare/runtime-types": ["@cloudflare/runtime-types@0.1.15", "", { "dependencies": { "miniflare": "5.20261019.2-beta", "workerd": "1.20261019.1" } }, "sha512-E8WCMGir4IRp/ksqodMWGpxHJvgtfgW9lbVM5mNAdWrnonMNOtGocyxVt6hG8/RGW9sjFWR7F7Cp3SCF1Cz6zg=="], + + "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20261019.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-5ZILbAta/fBQvhmAYeCE7dKMMYIr7N9wJEX50fEGLKSIwGtvT0HzSfHNIK/O7cBQ4gHQ2WfIiPClvwcye6E0fA=="], + + "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20261019.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cww2Ab6lVMxpTiEEjKmPI58p5EMo0vYTZSH+3WkqXJdQgc3ES66GuL247+U2B2YWajhhu2tqj7jySrXneU749g=="], + + "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20261019.1", "", { "os": "linux", "cpu": "x64" }, "sha512-4++VRi9vkQROYqrpZYOixIJuVDmujkwrT4kszsxNFlvcmsX6Mr2GumvxIN3gIixS3vJUh3Umdtkthqj5X0S3Xw=="], + + "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20261019.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-75PVtJOwfjScQ53sbLtQXI7D8yP1c21t+WEUPaYbqkzaz4ZqfR5wAd+zuWs8G5Q7kOj2SQIvEfY5Sn8Y+UVrnA=="], + + "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20261019.1", "", { "os": "win32", "cpu": "x64" }, "sha512-z49fvKTETMCnG6wnMcQwxx9CpryFceiKz04FRjq26EkhCyzRNJ6c3UXhyZz3r5f1CXnnBZc5DcU8ZouPkUJCqw=="], + + "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="], + + "@ctrl/tinycolor": ["@ctrl/tinycolor@4.2.1", "", {}, "sha512-Vh5uy9Y1JBMId6J1f68TMXnZrTCvNdo9Fu2k0d1YnJngR+sM5kWTKl441c/3TFs+cG0NWOjRWgQ/iaWhbYx+7A=="], + + "@emmetio/abbreviation": ["@emmetio/abbreviation@2.3.3", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA=="], + + "@emmetio/css-abbreviation": ["@emmetio/css-abbreviation@2.1.8", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw=="], + + "@emmetio/css-parser": ["@emmetio/css-parser@0.4.1", "", { "dependencies": { "@emmetio/stream-reader": "^2.2.0", "@emmetio/stream-reader-utils": "^0.1.0" } }, "sha512-2bC6m0MV/voF4CTZiAbG5MWKbq5EBmDPKu9Sb7s7nVcEzNQlrZP6mFFFlIaISM8X6514H9shWMme1fCm8cWAfQ=="], + + "@emmetio/html-matcher": ["@emmetio/html-matcher@1.3.0", "", { "dependencies": { "@emmetio/scanner": "^1.0.0" } }, "sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ=="], + + "@emmetio/scanner": ["@emmetio/scanner@1.0.4", "", {}, "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA=="], + + "@emmetio/stream-reader": ["@emmetio/stream-reader@2.2.0", "", {}, "sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw=="], + + "@emmetio/stream-reader-utils": ["@emmetio/stream-reader-utils@0.1.0", "", {}, "sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A=="], + + "@emnapi/core": ["@emnapi/core@1.11.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.3", "tslib": "^2.4.0" } }, "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="], + + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g=="], + + "@expressive-code/core": ["@expressive-code/core@0.44.3", "", { "dependencies": { "@ctrl/tinycolor": "^4.0.4", "hast-util-select": "^6.0.2", "hast-util-to-html": "^9.0.1", "hast-util-to-text": "^4.0.1", "hastscript": "^9.0.0", "postcss": "^8.4.38", "postcss-nested": "^7.0.2", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1" } }, "sha512-pffktF0TJoxC08dgNcutl/0LcSnTrjF6vXR39qVpwZ0EyxpwO9zjI7dbni6sO7w3yHpRBO0voLFvhzB+EPJRsA=="], + + "@expressive-code/plugin-frames": ["@expressive-code/plugin-frames@0.44.3", "", { "dependencies": { "@expressive-code/core": "^0.44.3" } }, "sha512-ysW4VXk9vXEliA99Ur6ApUepVtx74sPolp6o/j45BRjZQoL5O6kXKMhSP4koQLih8HbC5B3ImIvO+HKKhkv/oA=="], + + "@expressive-code/plugin-shiki": ["@expressive-code/plugin-shiki@0.44.3", "", { "dependencies": { "@expressive-code/core": "^0.44.3", "shiki": "^4.0.2" } }, "sha512-jAibAZPG7lDmi61EFJADZjoSZzjHiwZZdbbgR/mTlWXM4ON/Pyo/9tB2vrqQNKylsci6jqtCi6idrwqQXF53xA=="], + + "@expressive-code/plugin-text-markers": ["@expressive-code/plugin-text-markers@0.44.3", "", { "dependencies": { "@expressive-code/core": "^0.44.3" } }, "sha512-4Ae2xRSDV5kdWPsi40xWbOtoihbzM0zLVjQKkDAnD5KzFXl+6/kd+VhuWOlTRVaJ5cqGk++vCbAaf0MR1XbZlw=="], + + "@humanwhocodes/momoa": ["@humanwhocodes/momoa@2.0.4", "", {}, "sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA=="], + + "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], + + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg=="], + + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.4" }, "os": "darwin", "cpu": "x64" }, "sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw=="], + + "@img/sharp-freebsd-wasm32": ["@img/sharp-freebsd-wasm32@0.35.5", "", { "dependencies": { "@img/sharp-wasm32": "0.35.5" }, "os": "freebsd" }, "sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw=="], + + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw=="], + + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ=="], + + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.4", "", { "os": "linux", "cpu": "arm", "libc": "glibc" }, "sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw=="], + + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.4", "", { "os": "linux", "cpu": "arm64", "libc": "glibc" }, "sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A=="], + + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.3.4", "", { "os": "linux", "cpu": "ppc64", "libc": "glibc" }, "sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ=="], + + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.3.4", "", { "os": "linux", "cpu": "none", "libc": "glibc" }, "sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q=="], + + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.4", "", { "os": "linux", "cpu": "s390x", "libc": "glibc" }, "sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ=="], + + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.4", "", { "os": "linux", "cpu": "x64", "libc": "glibc" }, "sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw=="], + + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.4", "", { "os": "linux", "cpu": "arm64", "libc": "musl" }, "sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA=="], + + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.4", "", { "os": "linux", "cpu": "x64", "libc": "musl" }, "sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg=="], + + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.4" }, "os": "linux", "cpu": "arm", "libc": "glibc" }, "sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ=="], + + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.4" }, "os": "linux", "cpu": "arm64", "libc": "glibc" }, "sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ=="], + + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.3.4" }, "os": "linux", "cpu": "ppc64", "libc": "glibc" }, "sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA=="], + + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.3.4" }, "os": "linux", "cpu": "none", "libc": "glibc" }, "sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ=="], + + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.4" }, "os": "linux", "cpu": "s390x", "libc": "glibc" }, "sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA=="], + + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.4" }, "os": "linux", "cpu": "x64", "libc": "glibc" }, "sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog=="], + + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.4" }, "os": "linux", "cpu": "arm64", "libc": "musl" }, "sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg=="], + + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.4" }, "os": "linux", "cpu": "x64", "libc": "musl" }, "sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A=="], + + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.5", "", { "dependencies": { "@emnapi/runtime": "^1.11.3" } }, "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ=="], + + "@img/sharp-webcontainers-wasm32": ["@img/sharp-webcontainers-wasm32@0.35.5", "", { "dependencies": { "@img/sharp-wasm32": "0.35.5" }, "cpu": "none" }, "sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA=="], + + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.35.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.5", "", { "os": "win32", "cpu": "x64" }, "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.6.0", "", {}, "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], + + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.2.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" } }, "sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g=="], + + "@oslojs/encoding": ["@oslojs/encoding@1.1.0", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="], + + "@oxc-project/types": ["@oxc-project/types@0.155.0", "", {}, "sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q=="], + + "@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.73.0", "", { "os": "android", "cpu": "arm" }, "sha512-dhunAdMbi/4o0Y66/+Wl6rpsNqpvE4i26Qw6bB77A97L2lJoktWg6m+X9QOon8iOEEqzD9rEZqFi5uZ4oIzhCA=="], + + "@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.73.0", "", { "os": "android", "cpu": "arm64" }, "sha512-dBoypjC2/xeyKyIhN2waX0/XeoccH9k0lVVe+sGRc4X8HSJVRtqyZDoXdr7bGY9A75c51dX0z0dLDpGQKwCdCg=="], + + "@oxfmt/binding-darwin-arm64": ["@oxfmt/binding-darwin-arm64@0.73.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Tn7Y+qWIpCQK+qdYwBcn6YvqeBuPiuG3tcNlYg16a5QL2o3rSmrgBB1nCyYqo0uvc1LNrfQW9/CK2tzFuID/Fg=="], + + "@oxfmt/binding-darwin-x64": ["@oxfmt/binding-darwin-x64@0.73.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-yVb0E0tg/+9Xc9YJCBq66qF1DC/aN5mhb5nnHz0k4ikwkMANtSul2VPDSwjYdXR6/DFimc+pPUgLBqkIa5Ctuw=="], + + "@oxfmt/binding-freebsd-x64": ["@oxfmt/binding-freebsd-x64@0.73.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-IKR4lFnBkYNL9Uqdxa5GMTQLvnLj+3sDwTvh2atHCzah0xeo+1xR5bigRU8uyXlOsA4VpXUn0UAimq4E4RBGmQ=="], + + "@oxfmt/binding-linux-arm-gnueabihf": ["@oxfmt/binding-linux-arm-gnueabihf@0.73.0", "", { "os": "linux", "cpu": "arm" }, "sha512-7qnZ4g0ZCO3aZevOOQpZIABYwwXJW1lLDo2elviGdPQ0i11255xKEMFsi97fC1/nauQFseCJfELUy/k2JoOP+g=="], + + "@oxfmt/binding-linux-arm-musleabihf": ["@oxfmt/binding-linux-arm-musleabihf@0.73.0", "", { "os": "linux", "cpu": "arm" }, "sha512-36eBico2j/WPB84gb4FYd/jjqGCKOwU4M+TWLBUIqlnzTozQueLy0joA5BwsSp2OZFVkOlpCwvA3ndPamdiyLw=="], + + "@oxfmt/binding-linux-arm64-gnu": ["@oxfmt/binding-linux-arm64-gnu@0.73.0", "", { "os": "linux", "cpu": "arm64", "libc": "glibc" }, "sha512-VZ3cSgCAT18knKo52DcACmAuIoqG6qVEyeZRFbJd0xmYQ8GyYjuBc6IAh6MZHijT6q7p4/HPI+6ZWsCuhdV6iw=="], + + "@oxfmt/binding-linux-arm64-musl": ["@oxfmt/binding-linux-arm64-musl@0.73.0", "", { "os": "linux", "cpu": "arm64", "libc": "musl" }, "sha512-8YVzH59XSHcqwXNgc5qK851zCqwpmBgOifARXEPPe5/xTsIbHceYoPZAyqfpYV0CfKmzSaEDBgYHFza4rhx+Zw=="], + + "@oxfmt/binding-linux-ppc64-gnu": ["@oxfmt/binding-linux-ppc64-gnu@0.73.0", "", { "os": "linux", "cpu": "ppc64", "libc": "glibc" }, "sha512-MqRJnmCys0rwOgpWyEidSA7v/bvOqfNDJhPkQoR9mPVYrvSmMaW2Xhdpl28cbTA+brOM1is62GjIvJplVlUVpw=="], + + "@oxfmt/binding-linux-riscv64-gnu": ["@oxfmt/binding-linux-riscv64-gnu@0.73.0", "", { "os": "linux", "cpu": "none", "libc": "glibc" }, "sha512-4t2F3SWJSK/O/kRRFnL0elJzn75fJGGu71gs1HtiWcS80SmJOpkHiwwLnTp9suGdns8VHEPxYS0GTcyVwHKWBA=="], + + "@oxfmt/binding-linux-riscv64-musl": ["@oxfmt/binding-linux-riscv64-musl@0.73.0", "", { "os": "linux", "cpu": "none", "libc": "musl" }, "sha512-D8b4I8I1kLdJQFwdFXNV+cwh3xWSiNKofjdGUk0nDpc0AHOds8T+poGbP9RWQMvgsvMb9OMgp/aV7ahBR37TbQ=="], + + "@oxfmt/binding-linux-s390x-gnu": ["@oxfmt/binding-linux-s390x-gnu@0.73.0", "", { "os": "linux", "cpu": "s390x", "libc": "glibc" }, "sha512-QrC1S5VL1+N5rvorB15jH7CPvjVHA7GyUI5oVEd8Bd9m5605RDBQYTxDQ920DuylRoZOh2V7/xXpfCtFUrojUQ=="], + + "@oxfmt/binding-linux-x64-gnu": ["@oxfmt/binding-linux-x64-gnu@0.73.0", "", { "os": "linux", "cpu": "x64", "libc": "glibc" }, "sha512-dFcUKQPltCEPa+YlkF3zqufn6nJPZdG73LpnjBvZFFsU3laCMssDXSYB99mVaj43FqdEgjpz5TO6gbYXcczd9g=="], + + "@oxfmt/binding-linux-x64-musl": ["@oxfmt/binding-linux-x64-musl@0.73.0", "", { "os": "linux", "cpu": "x64", "libc": "musl" }, "sha512-LmQe+nV8e+eUwfPNCxDF+owbsg88toBiR/8rZRgVOeKQP3MaGefXZRvBoc1UG37iWEQ/9JlCXG9Mc4nf4N3agw=="], + + "@oxfmt/binding-openharmony-arm64": ["@oxfmt/binding-openharmony-arm64@0.73.0", "", { "os": "none", "cpu": "arm64" }, "sha512-8weOr1OKknE0G2DbR6ejJUT9AOsoBU6cXlsetmbihHv1K+bK7qP61T6lJxY0k/aAO7GyOJiRGNlL+04r4eH8Xg=="], + + "@oxfmt/binding-win32-arm64-msvc": ["@oxfmt/binding-win32-arm64-msvc@0.73.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-mZV+QxXYgb8E18gzAFItb937VIsE4dqWdStrD3tRRC52UubOYEuU+e7NaIqUsGdB5YsJqNeuck3Ue/af2r9wyA=="], + + "@oxfmt/binding-win32-ia32-msvc": ["@oxfmt/binding-win32-ia32-msvc@0.73.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-QJPUsbgUb97ZPPMkFUq6VQq0fLhC4Np19KL9aB/qKVwXnpKE6MIiILXqzhKnQ/UbnEDjKJ7Xv1d6zH4bYPCUdQ=="], + + "@oxfmt/binding-win32-x64-msvc": ["@oxfmt/binding-win32-x64-msvc@0.73.0", "", { "os": "win32", "cpu": "x64" }, "sha512-cGcze2fu+ykIaFtkEvZlkZ3HvnCLk+WISthtq99zWTWn0x13t5L9TxvGdLZMUMvtfYR+ICkiCwKnguVubdcw0w=="], + + "@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.89.0", "", { "os": "android", "cpu": "arm" }, "sha512-Pflpba5F3gRAQ12sWglRxvWTHEdAusNDvmi4Y5N1T5SY1AGkM7AixOnET8qkrnLJ/F32qAlNokO6eCknT8tmnw=="], + + "@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.89.0", "", { "os": "android", "cpu": "arm64" }, "sha512-dYmyD1x7LaPlb79vZWUcpTShk8dI9z1I+IubtqrBxWNJaDhQdPHwcsvg7q5StTU4xWyd9AwPfLaTYFZY8NZgtQ=="], + + "@oxlint/binding-darwin-arm64": ["@oxlint/binding-darwin-arm64@1.89.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Z/oxOPiIqiS7fItRdxaaOEC+6GD3OpGi0IxoR0dMefzQHolsKLzScw2DNcu/5gUPok3tv+G9mslvYB5bo3i0qg=="], + + "@oxlint/binding-darwin-x64": ["@oxlint/binding-darwin-x64@1.89.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-eAjTkHSjAxrdCPJxO6y5sPQL5pYLKb2kvxZz9UU7tXnOZk9I/HLtkvx9S7GHY883k+AmL/eC7mqFt+w7WCwk0w=="], + + "@oxlint/binding-freebsd-x64": ["@oxlint/binding-freebsd-x64@1.89.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0lNCA9Tl1n1FKpqy2/9ckp0kIuSrtJ/l9bMRgaEhOtM7TsOHshFuwOcvW8oA9teXLMOJT61LjEKWiaHDFGxF/w=="], + + "@oxlint/binding-linux-arm-gnueabihf": ["@oxlint/binding-linux-arm-gnueabihf@1.89.0", "", { "os": "linux", "cpu": "arm" }, "sha512-OTFEvi9W+JczaZD1Caj17SGCoW8H7TE+fvx0D7ATEWa1Lohn91UMxFQ6r145SiGy/yCxeIwptMys/RAM7gSVSQ=="], + + "@oxlint/binding-linux-arm-musleabihf": ["@oxlint/binding-linux-arm-musleabihf@1.89.0", "", { "os": "linux", "cpu": "arm" }, "sha512-fRxgNHvBLswj6MrhW2uBq5UDhPRQlCbHeq/Nf8yuG9zOxhDsXqX/mi2PX1axgsQxMm88el7HLiJAzglAnflYxw=="], + + "@oxlint/binding-linux-arm64-gnu": ["@oxlint/binding-linux-arm64-gnu@1.89.0", "", { "os": "linux", "cpu": "arm64", "libc": "glibc" }, "sha512-v9ENzHvwrQoYSWTtviH1pOYc7R9UovDxVofnck50cNmPihRRTcrg5X0UlRbLOZ7IVtxPOlh2hb7kDO+FpE8SfQ=="], + + "@oxlint/binding-linux-arm64-musl": ["@oxlint/binding-linux-arm64-musl@1.89.0", "", { "os": "linux", "cpu": "arm64", "libc": "musl" }, "sha512-vfffe8mQ484AzVadlUOFpgL+JDwhvs9hrzxTgQP1FVfi4ZEUUYeffXlzC9dFV7Dgnu5s/KrNKvfmG6HxBd/ISQ=="], + + "@oxlint/binding-linux-ppc64-gnu": ["@oxlint/binding-linux-ppc64-gnu@1.89.0", "", { "os": "linux", "cpu": "ppc64", "libc": "glibc" }, "sha512-Buonlp4hVS7RGNP5M7bPpGNQrenyzA2C64JcO/oz9C1c6dGOXJ7VEAnaiAMY7voou0JYyCJdvDVlFQc5DZX3DQ=="], + + "@oxlint/binding-linux-riscv64-gnu": ["@oxlint/binding-linux-riscv64-gnu@1.89.0", "", { "os": "linux", "cpu": "none", "libc": "glibc" }, "sha512-1eVwJJrJ3lYIPT+cm7BcBBZZXY6IQZP/oyonaruTor/MrVyWOpvnwt1oD5CLdsHg5aVBIxXodQSvt+1gK6ck5w=="], + + "@oxlint/binding-linux-riscv64-musl": ["@oxlint/binding-linux-riscv64-musl@1.89.0", "", { "os": "linux", "cpu": "none", "libc": "musl" }, "sha512-fRQQsKUGShdEL5Fl9o9uhj3grkZbeXKLv0icQ5tspUCNYSDy8wzzEcP1WC2bKdTQ0rSszbKlO3iiHORxV22DLg=="], + + "@oxlint/binding-linux-s390x-gnu": ["@oxlint/binding-linux-s390x-gnu@1.89.0", "", { "os": "linux", "cpu": "s390x", "libc": "glibc" }, "sha512-dEtYjIy6+fHcjKklsmtmMkZRhwHr2UbzjmpvuPtQYSeN9awQ2hx5JLms5Wvri+8P/NZtD8v5hfgHALUql+/Ycg=="], + + "@oxlint/binding-linux-x64-gnu": ["@oxlint/binding-linux-x64-gnu@1.89.0", "", { "os": "linux", "cpu": "x64", "libc": "glibc" }, "sha512-hqjwIj5KOYM8Wjef/p7IruNi1iByLhSzbMBk62y22P/E8q7Jj0Pp+DeUTKTRYyZYCDtU1wEGlnWOnttHbyNIww=="], + + "@oxlint/binding-linux-x64-musl": ["@oxlint/binding-linux-x64-musl@1.89.0", "", { "os": "linux", "cpu": "x64", "libc": "musl" }, "sha512-iAQN3e6o9UgUCcdGwqRx/jYcKbG3HK7ur3rH6lTqVcQeNGZ85AeWIg6cgn/edwFQ0ofEZZItMwkzyCVTahnvEA=="], + + "@oxlint/binding-openharmony-arm64": ["@oxlint/binding-openharmony-arm64@1.89.0", "", { "os": "none", "cpu": "arm64" }, "sha512-MNxjEByz3N/7Q4qFQuPWvc14TpN9LoD3pECEDsJTZbP/wyB7U+hduUXEl7QAbbgVezFwsb9sWtNUO6XVBndOhg=="], + + "@oxlint/binding-win32-arm64-msvc": ["@oxlint/binding-win32-arm64-msvc@1.89.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-xxe+i9wfuFso/VnoL4mfj0UlzE+cfCFiUB06vkXCMyQdgVVmItF5FfAF4tATAktSU6uHruWR+FjqvAT9cHZZzg=="], + + "@oxlint/binding-win32-ia32-msvc": ["@oxlint/binding-win32-ia32-msvc@1.89.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-saFy07cO94E664glwgLmKLI40HgTL1PY86y4T6+ud4niX4H0Kdqjcmqmjps7mQNdBytV5guGbXDdyQtRfeLbLA=="], + + "@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.89.0", "", { "os": "win32", "cpu": "x64" }, "sha512-hLFJ/o0f/dTJh/EW0vmcbREYSQO9tYgfiAAlsOkiLFiJuRRWvYtFELUXN5o2ijpSo/tlO1n+YlWN58tZwPG18A=="], + + "@pagefind/darwin-arm64": ["@pagefind/darwin-arm64@1.5.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-MXpI+7HsAdPkvJ0gk9xj9g541BCqBZOBbdwj9g6lB5LCj6kSV6nqDSjzcAJwvOsfu0fjwvC8hQU+ecfhp+MpiQ=="], + + "@pagefind/darwin-x64": ["@pagefind/darwin-x64@1.5.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-IojxFWMEJe0RQ7PQ3KXQsPIImNsbpPYpoZ+QUDrL8fAl/O27IX+LVLs74/UzEZy5uA2LD8Nz1AiwKr72vrkZQw=="], + + "@pagefind/default-ui": ["@pagefind/default-ui@1.5.2", "", {}, "sha512-pm1LMnQg8N2B3n2TnjKlhaFihpz6zTiA4HiGQ6/slKO/+8K9CAU5kcjdSSPgpuk1PMuuN4hxLipUIifnrkl3Sg=="], + + "@pagefind/freebsd-x64": ["@pagefind/freebsd-x64@1.5.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-7EVzo9+0w+2cbe671BtMj10UlNo83I+HrLVLfRxO731svHRJKUfJ/mo05gU14pe9PCfpKNQT8FS3Xc/oDN6pOA=="], + + "@pagefind/linux-arm64": ["@pagefind/linux-arm64@1.5.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Ovt9+K35sqzn8H3ZMXGwls4TD/wMJuvRtShHIsmUQREmaxjrDEX7gHckRCrwYJ4XE1H1p6HkLz3wukrAnsfXQw=="], + + "@pagefind/linux-x64": ["@pagefind/linux-x64@1.5.2", "", { "os": "linux", "cpu": "x64" }, "sha512-V+tFqHKXhQKq/WqPBD67AFy7scn1/aZID00ws4fSDd+1daSi5UHR9VVlRrOUYKxn3VuFQYRD7lYXdZK1WED1YA=="], + + "@pagefind/windows-arm64": ["@pagefind/windows-arm64@1.5.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-hN9Nh90fNW61nNRCW9ZyQrAj/mD0eRvmJ8NlTUzkbuW8kIzGJUi3cxjFkEcMZ5h/8FsKWD/VcouZl4yo1F7B6g=="], + + "@pagefind/windows-x64": ["@pagefind/windows-x64@1.5.2", "", { "os": "win32", "cpu": "x64" }, "sha512-Fa2Iyw7kaDRzGMfNYNUXNW2zbL5FQVDgSOcbDHdzBrDEdpqOqg8TcZ68F22ol6NJ9IGzvUdmeyZypLW5dyhqsg=="], + + "@poppinss/colors": ["@poppinss/colors@4.1.6", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg=="], + + "@poppinss/dumper": ["@poppinss/dumper@0.6.5", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw=="], + + "@poppinss/exception": ["@poppinss/exception@1.2.3", "", {}, "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw=="], + + "@readme/better-ajv-errors": ["@readme/better-ajv-errors@2.4.0", "", { "dependencies": { "@babel/code-frame": "^7.22.5", "@babel/runtime": "^7.22.5", "@humanwhocodes/momoa": "^2.0.3", "jsonpointer": "^5.0.0", "leven": "^3.1.0", "picocolors": "^1.1.1" }, "peerDependencies": { "ajv": "4.11.8 - 8" } }, "sha512-9WODaOAKSl/mU+MYNZ2aHCrkoRSvmQ+1YkLj589OEqqjOAhbn8j7Z+ilYoiTu/he6X63/clsxxAB4qny9/dDzg=="], + + "@readme/openapi-parser": ["@readme/openapi-parser@4.1.2", "", { "dependencies": { "@apidevtools/json-schema-ref-parser": "^13.0.5", "@readme/better-ajv-errors": "^2.3.2", "@readme/openapi-schemas": "^3.1.0", "@types/json-schema": "^7.0.15", "ajv": "^8.12.0", "ajv-draft-04": "^1.0.0" }, "peerDependencies": { "openapi-types": ">=7" } }, "sha512-lAFH88r/CHs5VZDUocEda0OSMSQsr6801sziIjOKyVA+0hSFN+BPuelPF5XvkMROHecnPd+XEJN1iNQqCgER/g=="], + + "@readme/openapi-schemas": ["@readme/openapi-schemas@3.1.0", "", {}, "sha512-9FC/6ho8uFa8fV50+FPy/ngWN53jaUu4GRXlAjcxIRrzhltJnpKkBG2Tp0IDraFJeWrOpk84RJ9EMEEYzaI1Bw=="], + + "@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.15", "", { "os": "android", "cpu": "arm" }, "sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw=="], + + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.15", "", { "os": "android", "cpu": "arm64" }, "sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg=="], + + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA=="], + + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A=="], + + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.15", "", { "os": "freebsd", "cpu": "x64" }, "sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ=="], + + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.15", "", { "os": "linux", "cpu": "arm" }, "sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ=="], + + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.15", "", { "os": "linux", "cpu": "arm64", "libc": "glibc" }, "sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw=="], + + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.15", "", { "os": "linux", "cpu": "arm64", "libc": "musl" }, "sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ=="], + + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.15", "", { "os": "linux", "cpu": "ppc64", "libc": "glibc" }, "sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw=="], + + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.15", "", { "os": "linux", "cpu": "s390x", "libc": "glibc" }, "sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA=="], + + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.15", "", { "os": "linux", "cpu": "x64", "libc": "glibc" }, "sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw=="], + + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.15", "", { "os": "linux", "cpu": "x64", "libc": "musl" }, "sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ=="], + + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.15", "", { "os": "none", "cpu": "arm64" }, "sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w=="], + + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg=="], + + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.15", "", { "os": "win32", "cpu": "x64" }, "sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ=="], + + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], + + "@shikijs/core": ["@shikijs/core@4.5.0", "", { "dependencies": { "@shikijs/primitive": "4.5.0", "@shikijs/types": "4.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.5", "hast-util-to-html": "^9.0.5" } }, "sha512-6CIgwq+bt2JZ3OSjb/nBkkwEf/CfpHcbam+Mg5Pllco7HAGRQ2/EFhZrrJtbyNTgKwknfVS+llsXGygiDGwJWw=="], + + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@4.5.0", "", { "dependencies": { "@shikijs/types": "4.5.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.6" } }, "sha512-Ed6UdEj84LyYkwnxM5CvSMWAD+id9ExTYri0/+x41U9zrgNifPSS9lZm0JqBP0VfYI8yr8tY5nbNq9+p9R0kFA=="], + + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@4.5.0", "", { "dependencies": { "@shikijs/types": "4.5.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-dKVmbJecVdB9recl8a/CU4jdpNXyjJQrIcesIkpS70s5HS02ohpnCSpMUbotPnEUzbAcLQ+S4hwZYbkBBiTcJw=="], + + "@shikijs/langs": ["@shikijs/langs@4.5.0", "", { "dependencies": { "@shikijs/types": "4.5.0" } }, "sha512-wwBTqwduXSz0X4v9Qa2EWeFaYJEw1clCncWjoRNQRbVDu3SPRTRWucnouYapKj3b8Q+sk/41Vbc4B5c8nuYzLA=="], + + "@shikijs/primitive": ["@shikijs/primitive@4.5.0", "", { "dependencies": { "@shikijs/types": "4.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.5" } }, "sha512-UM7ShlA8oz0OWxLLO8BmO9Ip6fVqdkt8yF0EH7nOCqHfrx1eEAzF0ZUVK7L+I8NEgeu1PEWSRlxaiPPeCb4iDg=="], + + "@shikijs/themes": ["@shikijs/themes@4.5.0", "", { "dependencies": { "@shikijs/types": "4.5.0" } }, "sha512-zGb87H3UBbpuI4yoh5UguB1XbU+2BDU3kflM7UfZ+Cy2hWo9lBtqjSVK20gmLY6ukYl040hAypGXMgud9vul8Q=="], + + "@shikijs/types": ["@shikijs/types@4.5.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.5" } }, "sha512-drtPrtD8N0DENyD2cyt6Ajy+IWsiCLgMxODwLTpvH1UsN6a9jf90tNTPjqwHvA+0Lzu9e0txk8DjfqH1p9UJRw=="], + + "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.1.0", "", {}, "sha512-cVBuhDQjBZaNMTwBquYfPydrUP+E3hE6oYGasx8h6ZrODIM7/r+gxLDXdd66oszbrggZHEntdE6j+5gYIVyMWQ=="], + + "@sindresorhus/is": ["@sindresorhus/is@7.2.0", "", {}, "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw=="], + + "@speed-highlight/core": ["@speed-highlight/core@1.2.24", "", {}, "sha512-qeW2e1l78afw8VhRPfPQ1Gjj+KU5XFQ/OFV5ti6eTa9bruO7mJyZtA4vw0ofqmA3tKCkROE9xLk3VZoeRc98nw=="], + + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.4", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A=="], + + "@types/bun": ["@types/bun@1.4.4", "", { "dependencies": { "bun-types": "1.4.4" } }, "sha512-9wkRDXUXK124pEnAutE6n/l18j+i9GC5IAHK8TmX/2nlySETmky0OU6yL5U8pW1AoKh++AN9auDCr647BfurjQ=="], + + "@types/debug": ["@types/debug@4.1.13", "", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="], + + "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], + + "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="], + + "@types/hast": ["@types/hast@3.0.5", "", { "dependencies": { "@types/unist": "*" } }, "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g=="], + + "@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], + + "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], + + "@types/nlcst": ["@types/nlcst@2.0.3", "", { "dependencies": { "@types/unist": "*" } }, "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA=="], + + "@types/node": ["@types/node@24.19.2", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-93d49wCWJkCyN87MxLpNN6KXPCwFw7bVUg79mGUV1OS40JGWwVCr8n61DXCSqz7CV6/VfPkb7xec7uP2M+QHWQ=="], + + "@types/sax": ["@types/sax@1.2.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A=="], + + "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + + "@ungap/structured-clone": ["@ungap/structured-clone@1.4.0", "", {}, "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ=="], + + "@volar/kit": ["@volar/kit@2.4.30", "", { "dependencies": { "@volar/language-service": "2.4.30", "@volar/typescript": "2.4.30", "typesafe-path": "^0.2.2", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, "peerDependencies": { "typescript": "<7" } }, "sha512-VocLtumfXnqnPQEWc9Yx7uC1uOh9gyjVNlrn2+6Zx/z9aM6yTLhPK8lSqM9Ilu81XRa48I00lcYJWLi2BLxx8Q=="], + + "@volar/language-core": ["@volar/language-core@2.4.30", "", { "dependencies": { "@volar/source-map": "2.4.30" } }, "sha512-73fjwEV8wat6aOZ6U/y/hqtE30ocrIWECg67b0txGczOMNw+MffLmvJ5fnFfN9drVUEYZzLdaNo6VAy6G6avMw=="], + + "@volar/language-server": ["@volar/language-server@2.4.30", "", { "dependencies": { "@volar/language-core": "2.4.30", "@volar/language-service": "2.4.30", "@volar/typescript": "2.4.30", "path-browserify": "^1.0.1", "request-light": "^0.7.0", "vscode-languageserver": "^9.0.1", "vscode-languageserver-protocol": "^3.17.5", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, "peerDependencies": { "typescript": "<7" }, "optionalPeers": ["typescript"] }, "sha512-tcP9fzzUBbsLIFHhgnRU4ncQjMn9G9Vax0ffkQmdCrXVEYqRroUepmyC5AVgiju5bCiLD6a55qen5iOTQHkb3g=="], + + "@volar/language-service": ["@volar/language-service@2.4.30", "", { "dependencies": { "@volar/language-core": "2.4.30", "vscode-languageserver-protocol": "^3.17.5", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, "peerDependencies": { "typescript": "<7" }, "optionalPeers": ["typescript"] }, "sha512-w/xDsASiqC71ggCOm7sTDXDNk/l72XEptodeRC68tgZ0e5QM4hM3zAqBGC7mBctvhrpYwKfMG+t9xcUo2hEFtA=="], + + "@volar/source-map": ["@volar/source-map@2.4.30", "", {}, "sha512-zym1ob/KwDDyN+0jGVtxwbxJSRRnUvv5HspCtz3yEfVVdCG4/dzxJktMGPAgemqRuVs81O0o/y0q3qOUErR8kg=="], + + "@volar/typescript": ["@volar/typescript@2.4.30", "", { "dependencies": { "@volar/language-core": "2.4.30", "@volar/language-service": "2.4.30", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" }, "peerDependencies": { "typescript": "<7" }, "optionalPeers": ["typescript"] }, "sha512-/DgA5tgYFPfrLFuSjIMXMGHk1LQv2+P8eny6+BhGNzFJELWIW7UZlc56ZLIRFhLsPpMfIFxM8/HvOPBggrjTZg=="], + + "@vscode/emmet-helper": ["@vscode/emmet-helper@2.11.0", "", { "dependencies": { "emmet": "^2.4.3", "jsonc-parser": "^2.3.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.15.1", "vscode-uri": "^3.0.8" } }, "sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw=="], + + "@vscode/l10n": ["@vscode/l10n@0.0.18", "", {}, "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ=="], + + "ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], + + "ajv-draft-04": ["ajv-draft-04@1.0.0", "", { "peerDependencies": { "ajv": "^8.5.0" }, "optionalPeers": ["ajv"] }, "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw=="], + + "ajv-i18n": ["ajv-i18n@4.2.0", "", { "peerDependencies": { "ajv": "^8.0.0-beta.0" } }, "sha512-v/ei2UkCEeuKNXh8RToiFsUclmU+G57LO1Oo22OagNMENIw+Yb8eMwvHu7Vn9fmkjJyv6XclhJ8TbuigSglPkg=="], + + "am-i-vibing": ["am-i-vibing@0.5.0", "", { "dependencies": { "process-ancestry": "^0.1.0" }, "bin": { "am-i-vibing": "dist/cli.mjs" } }, "sha512-KLcsDhqf1XRrJmJr+9I/C0EX2K+sGrAGZFgkmd84c8TJO9zJBu+/kFInjazu1KfJWFhWDKdRWkpBzEs7s35DVA=="], + + "ansi-regex": ["ansi-regex@6.4.0", "", {}, "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], + + "arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], + + "astro": ["astro@7.4.2", "", { "dependencies": { "@astrojs/compiler-rs": "^0.5.1", "@astrojs/internal-helpers": "0.13.0", "@astrojs/markdown-satteri": "0.4.5", "@astrojs/telemetry": "3.4.1", "@capsizecss/unpack": "^4.0.1", "@clack/prompts": "^1.8.1", "@oslojs/encoding": "^1.1.0", "am-i-vibing": "^0.5.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "ci-info": "^4.4.0", "clsx": "^2.1.1", "common-ancestor-path": "^2.0.0", "cookie": "^2.0.1", "devalue": "^6.0.2", "diff": "^9.0.0", "dset": "^3.1.4", "es-module-lexer": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.4.1", "get-tsconfig": "5.0.0-beta.6", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.3.0", "jsonc-parser": "^3.3.1", "magic-string": "^1.4.3", "magicast": "^0.5.5", "mrmime": "^2.0.1", "neotraverse": "^1.0.1", "obug": "^3.0.0", "p-limit": "^7.3.3", "p-queue": "^9.3.5", "package-manager-detector": "^1.9.0", "piccolore": "^0.1.3", "picomatch": "^4.0.7", "rolldown": "^1.2.13", "shiki": "^4.5.0", "smol-toml": "^1.9.1", "svgo": "^4.1.0", "tinyclip": "^1.0.3", "tinyexec": "^1.3.1", "tinyglobby": "^0.2.17", "tinyps": "^0.5.0", "ultrahtml": "^1.7.0", "unifont": "^1.0.3", "unstorage": "^1.17.5", "verkit": "^0.5.0", "vite": "^8.3.4", "vitefu": "^1.1.3", "xxhash-wasm": "^1.1.0", "yargs-parser": "^22.0.0", "zod": "^4.6.6" }, "optionalDependencies": { "sharp": "^0.35.4" }, "peerDependencies": { "@astrojs/markdown-remark": "^7.3.0" }, "optionalPeers": ["@astrojs/markdown-remark"], "bin": { "astro": "./bin/astro.mjs" } }, "sha512-FAH4cdDlFaVnQGjazLTEIC5vJqGlo5XANc1wIbzQHIqAjJTll/RWTUs+SPNW1JdezG4YGxMZllaE5A41T2kzhA=="], + + "astro-expressive-code": ["astro-expressive-code@0.44.3", "", { "dependencies": { "rehype-expressive-code": "^0.44.3", "url-extras": "^0.1.0" }, "peerDependencies": { "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta || ^7.0.0" } }, "sha512-LUpQmmvzr90JXP5QIAopgdjlOKLuT3x0wlPAUpc4OZtPtFS8sbflPcGPpL/lG3pQuUCQV2CXUFgYNBYNu9tphA=="], + + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], + + "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], + + "bcp-47": ["bcp-47@2.1.2", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-wsv86UuNFuPnVieKLyECTT+CP1WSAHeoIg5P3xu2kA9Z+7fVPoKt7dxzSeEhtJel0AI+NW3fIW3KtTKQKUCngg=="], + + "bcp-47-match": ["bcp-47-match@2.0.3", "", {}, "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ=="], + + "blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="], + + "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], + + "bun-types": ["bun-types@1.4.4", "", { "dependencies": { "@types/node": "*", "undici-types": "*" } }, "sha512-yjrJh5oGX7mMPKGd19YXyIjmEuj4G4zNC30bs7PM9Az7NQ3ndF3j6hbeRdvJTRQEhJ/FYQwW0a2moweEi2hMxQ=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + + "cf": ["cf@1.0.0-beta.21", "", { "dependencies": { "@cloudflare/build-output-utils": "0.9.4", "@cloudflare/config": "0.28.0", "@cloudflare/runtime-types": "0.1.15", "blake3-wasm": "2.1.5", "miniflare": "5.20261019.2-beta" }, "bin": { "cf": "./bin/cf", "cloudflare": "./bin/cf" } }, "sha512-rJqfa8zW7pN0m/fmc11xT3GzcXrqwhtZ9pizO3LZyZJjoOPR+h/us/+ajK0tXPhL+jHqaW/39ODQ481GDUyziQ=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], + + "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], + + "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], + + "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="], + + "chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + + "ci-info": ["ci-info@4.4.0", "", {}, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="], + + "cliui": ["cliui@9.0.1", "", { "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], + + "commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="], + + "common-ancestor-path": ["common-ancestor-path@2.0.0", "", {}, "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng=="], + + "cookie": ["cookie@2.0.1", "", {}, "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w=="], + + "cookie-es": ["cookie-es@1.2.3", "", {}, "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw=="], + + "crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="], + + "css-select": ["css-select@6.0.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^7.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "nth-check": "^2.1.1" } }, "sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw=="], + + "css-selector-parser": ["css-selector-parser@3.3.0", "", {}, "sha512-Y2asgMGFqJKF4fq4xHDSlFYIkeVfRsm69lQC1q9kbEsH5XtnINTMrweLkjYMeaUgiXBy/uvKeO/a1JHTNnmB2g=="], + + "css-tree": ["css-tree@3.2.1", "", { "dependencies": { "mdn-data": "2.27.1", "source-map-js": "^1.2.1" } }, "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA=="], + + "css-what": ["css-what@7.0.0", "", {}, "sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ=="], + + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + + "csso": ["csso@5.0.5", "", { "dependencies": { "css-tree": "~2.2.0" } }, "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], + + "defu": ["defu@6.1.7", "", {}, "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="], + + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "devalue": ["devalue@6.0.3", "", {}, "sha512-3woMEITftG1wKeO/4d/QXH1DVKctTSZg3jNGPpjp9hNr5Hos8MCFF5McTML5ZtgKDObm2R591B0spRp2IVernA=="], + + "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], + + "diff": ["diff@9.0.0", "", {}, "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw=="], + + "direction": ["direction@2.0.1", "", { "bin": { "direction": "cli.js" } }, "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA=="], + + "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], + + "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="], + + "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="], + + "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="], + + "dset": ["dset@3.1.4", "", {}, "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "duplexer": ["duplexer@0.1.2", "", {}, "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="], + + "emmet": ["emmet@2.4.11", "", { "dependencies": { "@emmetio/abbreviation": "^2.3.3", "@emmetio/css-abbreviation": "^2.1.8" } }, "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ=="], + + "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + + "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + + "error-stack-parser-es": ["error-stack-parser-es@1.0.5", "", {}, "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-module-lexer": ["es-module-lexer@3.0.4", "", {}, "sha512-BldNY5mwkVu+cQ5Ymn3e6hB9Sx8XHx4ZevWbiUyZbfKxJxYO27Hf0wfzkxIaD5CAJMaGl3YspKELj0ff/7nCMA=="], + + "es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "event-stream": ["event-stream@4.0.1", "", { "dependencies": { "duplexer": "^0.1.1", "from": "^0.1.7", "map-stream": "0.0.7", "pause-stream": "^0.0.11", "split": "^1.0.1", "stream-combiner": "^0.2.2", "through": "^2.3.8" } }, "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA=="], + + "eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="], + + "expressive-code": ["expressive-code@0.44.3", "", { "dependencies": { "@expressive-code/core": "^0.44.3", "@expressive-code/plugin-frames": "^0.44.3", "@expressive-code/plugin-shiki": "^0.44.3", "@expressive-code/plugin-text-markers": "^0.44.3" } }, "sha512-7RGslhJTNt01pdn5I3QmVh5C8ZIiSm0A+ORQ9j9r39oOgJVKjn/7dXl9rMT/SqmFLE6pJkTYI7UjW0BxJnW7OQ=="], + + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-string-truncated-width": ["fast-string-truncated-width@3.0.3", "", {}, "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g=="], + + "fast-string-width": ["fast-string-width@3.0.2", "", { "dependencies": { "fast-string-truncated-width": "^3.0.2" } }, "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg=="], + + "fast-uri": ["fast-uri@3.1.9", "", {}, "sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ=="], + + "fast-wrap-ansi": ["fast-wrap-ansi@0.2.2", "", { "dependencies": { "fast-string-width": "^3.0.2" } }, "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "flattie": ["flattie@1.1.1", "", {}, "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ=="], + + "fnv1a-64": ["fnv1a-64@0.1.2", "", {}, "sha512-mJbcILTPybAR1jdOwt/lVv8N2cffeJFFP+gVbSAwLEx1ujXH27RpPd8Rokec/KX9c76UYIB6Co+H4lQzxPnJfA=="], + + "fontace": ["fontace@0.4.1", "", { "dependencies": { "fontkitten": "^1.0.2" } }, "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw=="], + + "fontkitten": ["fontkitten@1.0.3", "", { "dependencies": { "tiny-inflate": "^1.0.3" } }, "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw=="], + + "form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="], + + "from": ["from@0.1.7", "", {}, "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin", "hasInstallScript": true }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "get-east-asian-width": ["get-east-asian-width@1.7.0", "", {}, "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-own-enumerable-property-symbols": ["get-own-enumerable-property-symbols@3.0.2", "", {}, "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "get-tsconfig": ["get-tsconfig@5.0.0-beta.6", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-X6fBC0pmImC70gvX2zm56go9hx0MyoGVdG0tUCkg/D+Xnh5TJsOZ7iDbOdI3PvmtrDxnu1YdDufpK2QJX1Meqw=="], + + "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "h3": ["h3@1.15.11", "", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="], + + "har-validator-compiled": ["har-validator-compiled@1.0.1", "", {}, "sha512-qWYNm1x4ew/a1wojPaywtF9QuO4GASWbVEARRt2EtKdTj7/j1qyJ7q6NhOUBNTRInlY8AD1duK8hxLWsXPtTSw=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], + + "hast-util-embedded": ["hast-util-embedded@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-is-element": "^3.0.0" } }, "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA=="], + + "hast-util-format": ["hast-util-format@1.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-embedded": "^3.0.0", "hast-util-minify-whitespace": "^1.0.0", "hast-util-phrasing": "^3.0.0", "hast-util-whitespace": "^3.0.0", "html-whitespace-sensitive-tag-names": "^3.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA=="], + + "hast-util-has-property": ["hast-util-has-property@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA=="], + + "hast-util-is-body-ok-link": ["hast-util-is-body-ok-link@3.0.1", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ=="], + + "hast-util-is-element": ["hast-util-is-element@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g=="], + + "hast-util-minify-whitespace": ["hast-util-minify-whitespace@1.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-embedded": "^3.0.0", "hast-util-is-element": "^3.0.0", "hast-util-whitespace": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw=="], + + "hast-util-parse-selector": ["hast-util-parse-selector@4.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A=="], + + "hast-util-phrasing": ["hast-util-phrasing@3.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-embedded": "^3.0.0", "hast-util-has-property": "^3.0.0", "hast-util-is-body-ok-link": "^3.0.0", "hast-util-is-element": "^3.0.0" } }, "sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ=="], + + "hast-util-select": ["hast-util-select@6.0.4", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "bcp-47-match": "^2.0.0", "comma-separated-tokens": "^2.0.0", "css-selector-parser": "^3.0.0", "devlop": "^1.0.0", "direction": "^2.0.0", "hast-util-has-property": "^3.0.0", "hast-util-to-string": "^3.0.0", "hast-util-whitespace": "^3.0.0", "nth-check": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw=="], + + "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], + + "hast-util-to-string": ["hast-util-to-string@3.0.1", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A=="], + + "hast-util-to-text": ["hast-util-to-text@4.0.3", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-5yrlWOvGFs6CqHriUpSsNrjzT/Tdo+IOQudbznxOn8K5qblp18MJyzOg8IgDOKQ8MYgQcJA7SusuCjSpDr8mSA=="], + + "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], + + "hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], + + "html-escaper": ["html-escaper@3.0.3", "", {}, "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="], + + "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], + + "html-whitespace-sensitive-tag-names": ["html-whitespace-sensitive-tag-names@3.0.1", "", {}, "sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA=="], + + "http-cache-semantics": ["http-cache-semantics@4.3.0", "", {}, "sha512-M5t5LlJpS1UHMjvwRQVdFHvPISGeLAxNcrWuJkeGh0KxsqCHZ1O3NXZU/8x7cD0BDcGW8kapxMKTvwlqrNkHkA=="], + + "httpsnippet": ["httpsnippet@3.0.10", "", { "dependencies": { "chalk": "^4.1.2", "event-stream": "4.0.1", "form-data": "4.0.4", "har-validator-compiled": "^1.0.0", "stringify-object": "3.3.0", "yargs": "^17.4.0" }, "bin": { "httpsnippet": "bin/httpsnippet" } }, "sha512-1P102HsVslaT3IVfuUfVwxenfbogFiihqosnaKUScd/sON1omdZZCWdmzm6baRz4u1Va0CTI/7svLckCu9iNBw=="], + + "i18next": ["i18next@26.5.10", "", { "peerDependencies": { "typescript": "^5 || ^6 || ^7" }, "optionalPeers": ["typescript"] }, "sha512-YS4EiLQhhk0wGThm4Zy+BL2c9rBNIK+kAbksi4l/B+D6kVRQLIFZgu2b/MU2LibJJHwWNTYoOYR52T+jfV8kPQ=="], + + "iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="], + + "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], + + "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="], + + "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="], + + "is-docker": ["is-docker@4.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="], + + "is-obj": ["is-obj@1.0.1", "", {}, "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg=="], + + "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], + + "is-regexp": ["is-regexp@1.0.0", "", {}, "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.3.2", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA=="], + + "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="], + + "jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="], + + "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], + + "klona": ["klona@2.0.6", "", {}, "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA=="], + + "leven": ["leven@3.1.0", "", {}, "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="], + + "lightningcss": ["lightningcss@1.33.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.33.0", "lightningcss-darwin-arm64": "1.33.0", "lightningcss-darwin-x64": "1.33.0", "lightningcss-freebsd-x64": "1.33.0", "lightningcss-linux-arm-gnueabihf": "1.33.0", "lightningcss-linux-arm64-gnu": "1.33.0", "lightningcss-linux-arm64-musl": "1.33.0", "lightningcss-linux-x64-gnu": "1.33.0", "lightningcss-linux-x64-musl": "1.33.0", "lightningcss-win32-arm64-msvc": "1.33.0", "lightningcss-win32-x64-msvc": "1.33.0" } }, "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.33.0", "", { "os": "android", "cpu": "arm64" }, "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.33.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.33.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.33.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.33.0", "", { "os": "linux", "cpu": "arm" }, "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.33.0", "", { "os": "linux", "cpu": "arm64", "libc": "glibc" }, "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.33.0", "", { "os": "linux", "cpu": "arm64", "libc": "musl" }, "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.33.0", "", { "os": "linux", "cpu": "x64", "libc": "glibc" }, "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.33.0", "", { "os": "linux", "cpu": "x64", "libc": "musl" }, "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.33.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.33.0", "", { "os": "win32", "cpu": "x64" }, "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA=="], + + "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], + + "lru-cache": ["lru-cache@11.5.3", "", {}, "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg=="], + + "magic-string": ["magic-string@1.4.3", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.6.0" } }, "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ=="], + + "magicast": ["magicast@0.5.5", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "source-map-js": "^1.2.1" } }, "sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA=="], + + "map-stream": ["map-stream@0.0.7", "", {}, "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "mdast-util-directive": ["mdast-util-directive@3.1.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-character": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Gqpa3MHorXYEZ0VfbroyvBlVKUfz371V6mIERqOYMcb0W1D3LwmFq1ZW4O2TDrbn+4ZmGLwMWeFRJgYZ+bc5fQ=="], + + "mdast-util-from-markdown": ["mdast-util-from-markdown@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-KRLgmPaP2H1ul2bpoJkOS+9pAcDAAj9Nu1mqLnh8NLeJG5tlvj42TH80PIGBpROWlRXLk0lDrWNuZCFy/tsB4A=="], + + "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="], + + "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="], + + "mdast-util-to-markdown": ["mdast-util-to-markdown@2.2.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-character": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-2Jn/ADrHFFyyniBMi0bncTbJqU78K3/TEYvzvwrWWzD9MYPUa/Gm4r3nOeKFQg/PDM5BsbDYdc8ackW4NTY6NA=="], + + "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], + + "mdn-data": ["mdn-data@2.27.1", "", {}, "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ=="], + + "micromark": ["micromark@4.0.3", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-edit-map": "^1.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oGYfQzHSG5dOMovQcJ3fyTmZlWAWpi0XA0sJwJs+i6OT88o1+Jtw/8z0CmdowSLxGhhFK89rQ/oXph/wN02PNw=="], + + "micromark-core-commonmark": ["micromark-core-commonmark@2.0.4", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.1.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-edit-map": "^1.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.3" } }, "sha512-wxEeE8v8XVvOrxn1TZj74qYhAtTQsSqufHVS3uNVyT1MupXxhyaHk8/9xDNPh9BmL77QNDgWTkZ3RsYHB2ry7w=="], + + "micromark-extension-directive": ["micromark-extension-directive@4.0.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "parse-entities": "^4.0.0" } }, "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg=="], + + "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="], + + "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="], + + "micromark-factory-space": ["micromark-factory-space@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-fS8hnLIjnjvdQIj39Geug8wWsR0HrYZ43KShKxNfwT7t2LOHo/LbWZEzEaSOjwtjdCsjoE6syHhonEzW0zv0+Q=="], + + "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="], + + "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="], + + "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], + + "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="], + + "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="], + + "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="], + + "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="], + + "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="], + + "micromark-util-edit-map": ["micromark-util-edit-map@1.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-Pa2ljlsEL6sVwFaYeyrOLSYbQt73JvGbPOYFq+9AElXiSnfI5Q4465RRZ1sHv7lDjDOnGRDDaqPXqClqWK/q1Q=="], + + "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], + + "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="], + + "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="], + + "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="], + + "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], + + "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="], + + "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], + + "micromark-util-types": ["micromark-util-types@2.0.3", "", {}, "sha512-oxB2Ik03hI0gv+VNn9tnh1t1YEe9MDPptViAEgfdf3YQHsn0pzGTgCdlSCJXcwhqm8phaEuM7zeEu3QQzVBrPg=="], + + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "miniflare": ["miniflare@5.20261019.2-beta", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "0.35.5", "undici": "7.29.1", "workerd": "1.20261019.1", "ws": "8.21.0", "youch": "4.1.0-beta.10" } }, "sha512-QNgCA/axizZ7kv7RV3nwbRoz+w4watBlwIsyXjwgYHw4eSw5BrR1F2152s3Kz+Bzy/YxjKFHrZl3LzBRbPgYoQ=="], + + "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "muggle-string": ["muggle-string@0.4.1", "", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="], + + "nanoid": ["nanoid@3.3.20", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ=="], + + "neotraverse": ["neotraverse@1.0.1", "", {}, "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w=="], + + "nlcst-to-string": ["nlcst-to-string@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0" } }, "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA=="], + + "node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="], + + "node-mock-http": ["node-mock-http@1.0.5", "", {}, "sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw=="], + + "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], + + "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], + + "obug": ["obug@3.0.0", "", {}, "sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw=="], + + "ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], + + "oniguruma-parser": ["oniguruma-parser@0.12.2", "", {}, "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw=="], + + "oniguruma-to-es": ["oniguruma-to-es@4.3.6", "", { "dependencies": { "oniguruma-parser": "^0.12.2", "regex": "^6.1.0", "regex-recursion": "^6.0.2" } }, "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA=="], + + "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], + + "oxfmt": ["oxfmt@0.73.0", "", { "dependencies": { "tinypool": "2.2.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.73.0", "@oxfmt/binding-android-arm64": "0.73.0", "@oxfmt/binding-darwin-arm64": "0.73.0", "@oxfmt/binding-darwin-x64": "0.73.0", "@oxfmt/binding-freebsd-x64": "0.73.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.73.0", "@oxfmt/binding-linux-arm-musleabihf": "0.73.0", "@oxfmt/binding-linux-arm64-gnu": "0.73.0", "@oxfmt/binding-linux-arm64-musl": "0.73.0", "@oxfmt/binding-linux-ppc64-gnu": "0.73.0", "@oxfmt/binding-linux-riscv64-gnu": "0.73.0", "@oxfmt/binding-linux-riscv64-musl": "0.73.0", "@oxfmt/binding-linux-s390x-gnu": "0.73.0", "@oxfmt/binding-linux-x64-gnu": "0.73.0", "@oxfmt/binding-linux-x64-musl": "0.73.0", "@oxfmt/binding-openharmony-arm64": "0.73.0", "@oxfmt/binding-win32-arm64-msvc": "0.73.0", "@oxfmt/binding-win32-ia32-msvc": "0.73.0", "@oxfmt/binding-win32-x64-msvc": "0.73.0" }, "peerDependencies": { "@astrojs/compiler-rs": "^0.4.0 || ^0.5.0", "svelte": "^5.0.0", "vite-plus": "*" }, "optionalPeers": ["@astrojs/compiler-rs", "svelte", "vite-plus"], "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-Ke4xbpuzqIBP4bkd48q815RhqWNJqOBzxVA9199M4qcF0y0LtpUv7UTg1dJ5ea6jw0RBUDZvX3qOUMNmmxfxbw=="], + + "oxlint": ["oxlint@1.89.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.89.0", "@oxlint/binding-android-arm64": "1.89.0", "@oxlint/binding-darwin-arm64": "1.89.0", "@oxlint/binding-darwin-x64": "1.89.0", "@oxlint/binding-freebsd-x64": "1.89.0", "@oxlint/binding-linux-arm-gnueabihf": "1.89.0", "@oxlint/binding-linux-arm-musleabihf": "1.89.0", "@oxlint/binding-linux-arm64-gnu": "1.89.0", "@oxlint/binding-linux-arm64-musl": "1.89.0", "@oxlint/binding-linux-ppc64-gnu": "1.89.0", "@oxlint/binding-linux-riscv64-gnu": "1.89.0", "@oxlint/binding-linux-riscv64-musl": "1.89.0", "@oxlint/binding-linux-s390x-gnu": "1.89.0", "@oxlint/binding-linux-x64-gnu": "1.89.0", "@oxlint/binding-linux-x64-musl": "1.89.0", "@oxlint/binding-openharmony-arm64": "1.89.0", "@oxlint/binding-win32-arm64-msvc": "1.89.0", "@oxlint/binding-win32-ia32-msvc": "1.89.0", "@oxlint/binding-win32-x64-msvc": "1.89.0" }, "peerDependencies": { "oxlint-tsgolint": ">=7.0.2003", "vite-plus": "*" }, "optionalPeers": ["oxlint-tsgolint", "vite-plus"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-a25hkfo61okGPvJz432fRIPoxlKtNnOVy9w4rzrmGgNm4iVTPwpSQlmdqNWnp6p9QyyPVfAZRgnf5XvSBZFk/A=="], + + "p-limit": ["p-limit@7.3.3", "", { "dependencies": { "yocto-queue": "^1.2.1" } }, "sha512-SKgVvDU5TNIxQ4r30U75BNLC6a+6GOEjngrk/ysM5+Zu3oJuk1SC5ApzIzY/7PjCEjoVPMWJ0zho/4QUiEB7TQ=="], + + "p-queue": ["p-queue@9.3.5", "", { "dependencies": { "eventemitter3": "^5.0.4", "p-timeout": "^7.0.0" } }, "sha512-Lki/zhXFVmDhesBUn/yHRLW9Bte7mE7xoFHzTR8sIomV7a2z+LxYAPkGvdfidNiu4fKSfjtawixxSHcZci5QcQ=="], + + "p-timeout": ["p-timeout@7.0.2", "", {}, "sha512-prbX4Z3YszrFNgH+MW5Zoeq3baXrMtP/MQnFeET90UB/GtGcGDQ5Usg9OCy6ETjTTntOw1SL2z9fMPUppN3Guw=="], + + "package-manager-detector": ["package-manager-detector@1.9.0", "", {}, "sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA=="], + + "pagefind": ["pagefind@1.5.2", "", { "optionalDependencies": { "@pagefind/darwin-arm64": "1.5.2", "@pagefind/darwin-x64": "1.5.2", "@pagefind/freebsd-x64": "1.5.2", "@pagefind/linux-arm64": "1.5.2", "@pagefind/linux-x64": "1.5.2", "@pagefind/windows-arm64": "1.5.2", "@pagefind/windows-x64": "1.5.2" }, "bin": { "pagefind": "lib/runner/bin.cjs" } }, "sha512-XTUaK0hXMCu2jszWE584JGQT7y284TmMV9l/HX3rnG5uo3rHI/uHU56XTyyyPFjeWEBxECbAi0CaFDJOONtG0Q=="], + + "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], + + "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], + + "pause-stream": ["pause-stream@0.0.11", "", { "dependencies": { "through": "~2.3" } }, "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A=="], + + "piccolore": ["piccolore@0.1.3", "", {}, "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="], + + "postcss": ["postcss@8.5.31", "", { "dependencies": { "nanoid": "^3.3.20", "picocolors": "^1.1.1", "source-map-js": "^1.2.2" } }, "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg=="], + + "postcss-nested": ["postcss-nested@7.0.2", "", { "dependencies": { "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-5osppouFc0VR9/VYzYxO03VaDa3e8F23Kfd6/9qcZTUI8P58GIYlArOET2Wq0ywSl2o2PjELhYOFI4W7l5QHKw=="], + + "postcss-selector-parser": ["postcss-selector-parser@7.1.7", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-wWBro/7hJ40ye6TPiXkUQoXKCw23xSsjriUvqd7Uls9Sso3oLiON1flc18cCdnjgCRXw4fBVqUJJeOdDTh3P3g=="], + + "prettier": ["prettier@3.9.10", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA=="], + + "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="], + + "process-ancestry": ["process-ancestry@0.1.0", "", {}, "sha512-tGqJW/UnclpYASFcM6Xh8D8l/BMtaQ9+CSG0vlJSJTcdMM4lDRv4c6H0Pdcsfted+bVczdYSfk2fdukg2gQkZg=="], + + "property-information": ["property-information@7.2.0", "", {}, "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg=="], + + "radix3": ["radix3@1.1.2", "", {}, "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="], + + "readdirp": ["readdirp@5.1.1", "", {}, "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA=="], + + "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="], + + "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="], + + "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], + + "rehype-expressive-code": ["rehype-expressive-code@0.44.3", "", { "dependencies": { "expressive-code": "^0.44.3" } }, "sha512-vaVuQPobFXvmtcHXQ5uqXZlzuuPnLyIuZcX/03sX1U/Qfj9fZrPYI9w66OSr/PUx6alzRYmreEwgYBbsVx8HsA=="], + + "remark-directive": ["remark-directive@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-directive": "^3.0.0", "micromark-extension-directive": "^4.0.0", "unified": "^11.0.0" } }, "sha512-7sxn4RfF1o3izevPV1DheyGDD6X4c9hrGpfdUpm7uC++dqrnJxIZVkk7CoKqcLm0VUMAuOol7Mno3m6g8cfMuA=="], + + "request-light": ["request-light@0.7.0", "", {}, "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q=="], + + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + + "retext-smartypants": ["retext-smartypants@6.2.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "nlcst-to-string": "^4.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ=="], + + "rolldown": ["rolldown@1.2.15", "", { "dependencies": { "@oxc-project/types": "=0.155.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm-eabi": "1.2.15", "@rolldown/binding-android-arm64": "1.2.15", "@rolldown/binding-darwin-arm64": "1.2.15", "@rolldown/binding-darwin-x64": "1.2.15", "@rolldown/binding-freebsd-x64": "1.2.15", "@rolldown/binding-linux-arm-gnueabihf": "1.2.15", "@rolldown/binding-linux-arm64-gnu": "1.2.15", "@rolldown/binding-linux-arm64-musl": "1.2.15", "@rolldown/binding-linux-ppc64-gnu": "1.2.15", "@rolldown/binding-linux-s390x-gnu": "1.2.15", "@rolldown/binding-linux-x64-gnu": "1.2.15", "@rolldown/binding-linux-x64-musl": "1.2.15", "@rolldown/binding-openharmony-arm64": "1.2.15", "@rolldown/binding-win32-arm64-msvc": "1.2.15", "@rolldown/binding-win32-x64-msvc": "1.2.15" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g=="], + + "satteri": ["satteri@0.10.5", "", { "dependencies": { "@types/estree-jsx": "^1.0.5", "@types/hast": "^3.0.5", "@types/mdast": "^4.0.4", "@types/unist": "^3.0.3" }, "optionalDependencies": { "@bruits/satteri-darwin-arm64": "0.10.5", "@bruits/satteri-darwin-x64": "0.10.5", "@bruits/satteri-linux-arm64-gnu": "0.10.5", "@bruits/satteri-linux-arm64-musl": "0.10.5", "@bruits/satteri-linux-x64-gnu": "0.10.5", "@bruits/satteri-linux-x64-musl": "0.10.5", "@bruits/satteri-wasm32-wasi": "0.10.5", "@bruits/satteri-win32-arm64-msvc": "0.10.5", "@bruits/satteri-win32-x64-msvc": "0.10.5" } }, "sha512-Ao1LKpAEa9Wdg0otgbVKViZHEq9ebdXe4DMrp3s9vQAU0HNIuHnFEuMuOcm0ZIXyV0Yzxj91NvhLpvXZJO/5ZQ=="], + + "sax": ["sax@1.6.1", "", {}, "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q=="], + + "semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], + + "sharp": ["sharp@0.35.5", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.5", "@img/sharp-darwin-x64": "0.35.5", "@img/sharp-freebsd-wasm32": "0.35.5", "@img/sharp-libvips-darwin-arm64": "1.3.4", "@img/sharp-libvips-darwin-x64": "1.3.4", "@img/sharp-libvips-linux-arm": "1.3.4", "@img/sharp-libvips-linux-arm64": "1.3.4", "@img/sharp-libvips-linux-ppc64": "1.3.4", "@img/sharp-libvips-linux-riscv64": "1.3.4", "@img/sharp-libvips-linux-s390x": "1.3.4", "@img/sharp-libvips-linux-x64": "1.3.4", "@img/sharp-libvips-linuxmusl-arm64": "1.3.4", "@img/sharp-libvips-linuxmusl-x64": "1.3.4", "@img/sharp-linux-arm": "0.35.5", "@img/sharp-linux-arm64": "0.35.5", "@img/sharp-linux-ppc64": "0.35.5", "@img/sharp-linux-riscv64": "0.35.5", "@img/sharp-linux-s390x": "0.35.5", "@img/sharp-linux-x64": "0.35.5", "@img/sharp-linuxmusl-arm64": "0.35.5", "@img/sharp-linuxmusl-x64": "0.35.5", "@img/sharp-webcontainers-wasm32": "0.35.5", "@img/sharp-win32-arm64": "0.35.5", "@img/sharp-win32-ia32": "0.35.5", "@img/sharp-win32-x64": "0.35.5" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g=="], + + "shiki": ["shiki@4.5.0", "", { "dependencies": { "@shikijs/core": "4.5.0", "@shikijs/engine-javascript": "4.5.0", "@shikijs/engine-oniguruma": "4.5.0", "@shikijs/langs": "4.5.0", "@shikijs/themes": "4.5.0", "@shikijs/types": "4.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.5" } }, "sha512-OcRW72ngQvc+MSVWBRIovC/BTW589iUE/Vxav7VeM4RcY9MUjqB+IR6ILG1k9nuW6LiG/nct9uma4/MBtnZbmQ=="], + + "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], + + "sitemap": ["sitemap@9.0.1", "", { "dependencies": { "@types/node": "^24.9.2", "@types/sax": "^1.2.1", "arg": "^5.0.0", "sax": "^1.4.1" }, "bin": { "sitemap": "dist/esm/cli.js" } }, "sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ=="], + + "smol-toml": ["smol-toml@1.9.1", "", {}, "sha512-j7WyPdf0CJoutMzMoPLF/bVXHtKZIT9Rb4eGeEbWw8X2t5oirjPgpL+OpaENZrILYaXqYwnVH+qgUwVbl+pUJQ=="], + + "source-map-js": ["source-map-js@1.2.2", "", {}, "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw=="], + + "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + + "split": ["split@1.0.1", "", { "dependencies": { "through": "2" } }, "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg=="], + + "starlight-openapi": ["starlight-openapi@0.27.0", "", { "dependencies": { "@readme/openapi-parser": "^4.1.2", "github-slugger": "^2.0.0", "httpsnippet": "^3.0.10", "url-template": "^3.1.1" }, "peerDependencies": { "@astrojs/markdown-satteri": ">=0.3.2", "@astrojs/starlight": ">=0.41.0", "astro": ">=7.0.2" } }, "sha512-M8oZ7CEgQ1d+lyyBHPin44HrBgpVLVzJhTQ+O7SCdUhMfgg6Zj2t6kgQb1BVs/e/HnZbKlWFZuOdKeHqh9jaYw=="], + + "std-env": ["std-env@4.3.0", "", {}, "sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag=="], + + "stream-combiner": ["stream-combiner@0.2.2", "", { "dependencies": { "duplexer": "~0.1.1", "through": "~2.3.4" } }, "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ=="], + + "string-width": ["string-width@8.3.0", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ=="], + + "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], + + "stringify-object": ["stringify-object@3.3.0", "", { "dependencies": { "get-own-enumerable-property-symbols": "^3.0.0", "is-obj": "^1.0.1", "is-regexp": "^1.0.0" } }, "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw=="], + + "strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "svgo": ["svgo@4.1.0", "", { "dependencies": { "commander": "^11.1.0", "css-select": "^6.0.0", "css-tree": "^3.0.1", "css-what": "^7.0.0", "csso": "^5.0.5", "picocolors": "^1.1.1", "sax": "1.6.1" }, "bin": { "svgo": "bin/svgo.js" } }, "sha512-bkxnTg1kSU0guhIBmibA6UUhrQmPVA1XsQLN+ylCd+UWzbnLkySOcXpyk1mrl05f+pcaCx2eHb+sp6BgMZWX+Q=="], + + "through": ["through@2.3.8", "", {}, "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="], + + "tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="], + + "tinyclip": ["tinyclip@1.0.3", "", {}, "sha512-nTv2raKFMzGG6JJp0lpNDsRPdfFE6UHlu2rVEjXo8vkx1ZS3LW2CVXqIaLu8xZlu+6tbFs9V58Qc/Jmk/Ii7wA=="], + + "tinyexec": ["tinyexec@1.3.1", "", {}, "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA=="], + + "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], + + "tinypool": ["tinypool@2.2.0", "", {}, "sha512-jBrmx4lYmaC9k/mgPbylxs7kBUxHtD8256up+HjLaDFfXScKJQyil+SWXSvhAtT7XHo+yTVlpB81PGmHP8oLSQ=="], + + "tinyps": ["tinyps@0.5.1", "", {}, "sha512-B6Ykif5HflC3AdDkS4Zst/2muYVfeXoVMy+KorqH80s/1x4vunIORpNySH681X7fjj+dFM9MDKD6/Hsccjb87Q=="], + + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], + + "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "typesafe-path": ["typesafe-path@0.2.2", "", {}, "sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA=="], + + "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], + + "typescript-auto-import-cache": ["typescript-auto-import-cache@0.3.6", "", { "dependencies": { "semver": "^7.3.8" } }, "sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ=="], + + "ufo": ["ufo@1.6.4", "", {}, "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA=="], + + "ultrahtml": ["ultrahtml@1.7.0", "", {}, "sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g=="], + + "uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="], + + "undici": ["undici@7.29.1", "", {}, "sha512-RYONW2MeafgYlkVOKYKkA/Ag7BmXqgIWCa8t1m0JcxrQg9pI9lEqRhAOruOBCbAohOa/gkCF+iPi9hrgvTzu6Q=="], + + "undici-types": ["undici-types@7.24.6", "", {}, "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg=="], + + "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], + + "unifont": ["unifont@1.0.3", "", { "dependencies": { "css-tree": "^3.1.0", "fnv1a-64": "^0.1.2", "std-env": "^4.2.0", "undici": "^8.0.0" } }, "sha512-XGnEwj5sWdCbiBX01mWLSBncS0yMRjnLygau3IfdOlfjMacNIQvTGW4VH7pWBq+gGWArfSef/CensMLjMmIr/A=="], + + "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], + + "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], + + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], + + "unist-util-visit": ["unist-util-visit@5.2.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.1.0" } }, "sha512-fpsYs/VVoZxfGY9/j3FQpZmN8wdLJaV1Pt38AaJNZu9uKIHcFU3halCdBveEq1HGKO1OEfJY65w1zvoFV1b3qQ=="], + + "unist-util-visit-parents": ["unist-util-visit-parents@6.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-vKXxz6O38sKDj1mtx/k2cWvkkFBuYGx6uGrlzBkVeWCRF/F8sdI0j3Wbkz/5rVNRRuVc8UrgRdbCxNhmYXgP3w=="], + + "unstorage": ["unstorage@1.17.6", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.11", "lru-cache": "^11.5.3", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.4" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-gjKtcmn/KwZuJZO9o2sc0esAQnhmTw0LviDHwevTXbJMYs0dRTp8ZEEWXHU0vTpzbN3cA0yxlTeyXssMp3zDqw=="], + + "url-extras": ["url-extras@0.1.0", "", {}, "sha512-8tzwTeXFPuX/5PHuCDQE5Dd9Ts4rwoq2t9aIT+HS4iAVpmj5l4Ao7Q+BuuFjvWRqrLswBhQDk8O96ZicgCqQqw=="], + + "url-template": ["url-template@3.1.1", "", {}, "sha512-4oszoaEKE/mQOtAmdMWqIRHmkxWkUZMnXFnjQ5i01CuRSK3uluxcH1MRVVVWmhlnzT1SCDfKxxficm2G37qzCA=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "verkit": ["verkit@0.5.0", "", {}, "sha512-u4J1hr6Y+C/hit34GwL3eIcUf3wngT8TlYUjK7tk0jxydKLAq3sYvifjdTDA6U3KCKRcQCQDGf/srjxfZF3l7g=="], + + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], + + "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], + + "vite": ["vite@8.3.5", "", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.7", "postcss": "^8.5.30", "rolldown": "~1.2.14", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.7.1 || ^0.8.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ=="], + + "vitefu": ["vitefu@1.1.3", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["vite"] }, "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg=="], + + "volar-service-css": ["volar-service-css@0.0.71", "", { "dependencies": { "vscode-css-languageservice": "^6.3.0", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-wRRFt9BpjMKCazcgOh67MSjUjiWUCAh99DyYSDIOTuxaRjEtDC7PpB0k1Y1wbJIW/pVtMUSVbpPo3UGSm0Byxw=="], + + "volar-service-emmet": ["volar-service-emmet@0.0.71", "", { "dependencies": { "@emmetio/css-parser": "^0.4.1", "@emmetio/html-matcher": "^1.3.0", "@vscode/emmet-helper": "^2.9.3", "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-zqjzt6bN95e3CUstBm0PBFAJnrfz0ZAARka87fart46/gNCLLuP3Vujy8V/J8HEziTFLnfkgIASLFYPUhonJcA=="], + + "volar-service-html": ["volar-service-html@0.0.71", "", { "dependencies": { "vscode-html-languageservice": "^5.3.0", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-e8tHPhgQ7ooLfudAEIku+kgd9pWkq3SSz8RbnQDI1+Eb8wbenkLGHqoirLqz5ORLV6wIMr2Iv08RWBG5eOcgpw=="], + + "volar-service-prettier": ["volar-service-prettier@0.0.71", "", { "dependencies": { "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0", "prettier": "^2.2 || ^3.0" }, "optionalPeers": ["@volar/language-service", "prettier"] }, "sha512-Rz7JVH3qD108UCdmIEiZvOBNljMt2nLFdbN8AXcDfn7xD9F5I2aCIsDVqBbXw21PsnxG0b7MfwtNF+zPS/NKUg=="], + + "volar-service-typescript": ["volar-service-typescript@0.0.71", "", { "dependencies": { "path-browserify": "^1.0.1", "semver": "^7.6.2", "typescript-auto-import-cache": "^0.3.5", "vscode-languageserver-textdocument": "^1.0.11", "vscode-nls": "^5.2.0", "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-yTtM/BVT6hoyEYnDtaCyAtNhdNeS/mhTTABlBOdw3NNiRBUin3IznFJpgfjer4c6RYopiPjjQjc9VFhxVl1mLw=="], + + "volar-service-typescript-twoslash-queries": ["volar-service-typescript-twoslash-queries@0.0.71", "", { "dependencies": { "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-9K2k72s4n7rV9s4bX0MyjbX9iBribvKZbBJKuEmTCZfeWJXs6Yh7bGpY4eoc7UufAjvpheBqwyZCOIPBvxCv0A=="], + + "volar-service-yaml": ["volar-service-yaml@0.0.71", "", { "dependencies": { "vscode-uri": "^3.0.8", "yaml-language-server": "~1.23.0" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-qYGWGuVpUTnZGu5P/CR4KLK4aIR8RrcVnmfZ2eRcj9q/I8VZCoC5yy9FtEvfNvnDp4MU17yhdJcvpQPIqhJS2Q=="], + + "vscode-css-languageservice": ["vscode-css-languageservice@6.3.10", "", { "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "3.17.5", "vscode-uri": "^3.1.0" } }, "sha512-eq5N9Er3fC4vA9zd9EFhyBG90wtCCuXgRSpAndaOgXMh1Wgep5lBgRIeDgjZBW9pa+332yC9+49cZMW8jcL3MA=="], + + "vscode-html-languageservice": ["vscode-html-languageservice@5.6.2", "", { "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "^3.17.5", "vscode-uri": "^3.1.0" } }, "sha512-ulCrSnFnfQ16YzvwnYUgEbUEl/ZG7u2eV27YhvLObSHKkb8fw1Z9cgsnUwjTEeDIdJDoTDTDpxuhQwoenoLNMg=="], + + "vscode-json-languageservice": ["vscode-json-languageservice@4.1.8", "", { "dependencies": { "jsonc-parser": "^3.0.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.16.0", "vscode-nls": "^5.0.0", "vscode-uri": "^3.0.2" } }, "sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg=="], + + "vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="], + + "vscode-languageserver": ["vscode-languageserver@9.0.1", "", { "dependencies": { "vscode-languageserver-protocol": "3.17.5" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g=="], + + "vscode-languageserver-protocol": ["vscode-languageserver-protocol@3.17.5", "", { "dependencies": { "vscode-jsonrpc": "8.2.0", "vscode-languageserver-types": "3.17.5" } }, "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg=="], + + "vscode-languageserver-textdocument": ["vscode-languageserver-textdocument@1.0.16", "", {}, "sha512-qeNEuOi9a3qkqRf7Gfez2WL2VwlBTLCNekHBGO9kxPVUp9XuY15yfTJ9jwvOwKI0I2MhUUfZq95NSp+LK1CLQg=="], + + "vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="], + + "vscode-nls": ["vscode-nls@5.2.0", "", {}, "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng=="], + + "vscode-uri": ["vscode-uri@3.2.0", "", {}, "sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg=="], + + "workerd": ["workerd@1.20261019.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20261019.1", "@cloudflare/workerd-darwin-arm64": "1.20261019.1", "@cloudflare/workerd-linux-64": "1.20261019.1", "@cloudflare/workerd-linux-arm64": "1.20261019.1", "@cloudflare/workerd-windows-64": "1.20261019.1" }, "hasInstallScript": true, "bin": { "workerd": "bin/workerd" } }, "sha512-UUYRUq/7DmeCl11YriNbcVbjUZVLtt8uuCXFu+nz77hGFf8d4DEVXoCYtnA+MlBatE4+wxoj47DVqzK3xGBAiw=="], + + "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], + + "ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], + + "xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="], + + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + + "yaml": ["yaml@2.9.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw=="], + + "yaml-language-server": ["yaml-language-server@1.23.0", "", { "dependencies": { "@vscode/l10n": "^0.0.18", "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "ajv-i18n": "^4.2.0", "prettier": "^3.8.1", "request-light": "^0.5.7", "vscode-json-languageservice": "4.1.8", "vscode-languageserver": "^9.0.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.16.0", "vscode-uri": "^3.0.2", "yaml": "2.8.3" }, "bin": { "yaml-language-server": "bin/yaml-language-server" } }, "sha512-3qVyCOexLCWw06PQa5kRPwvMWMZ/eZeCRWUvgD6a0OkqL/4iCnxy2WumbWifa937Uo5xhyWJ0uxlU39ljhNh7A=="], + + "yargs": ["yargs@18.2.0", "", { "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "string-width": "^8.2.1", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" } }, "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w=="], + + "yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="], + + "yocto-queue": ["yocto-queue@1.2.2", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="], + + "youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="], + + "youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="], + + "zod": ["zod@4.6.7", "", {}, "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg=="], + + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + + "@astrojs/compiler-binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], + + "@astrojs/compiler-binding-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.2.6", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1 || 2.0.0-beta.2", "@emnapi/runtime": "^1.7.1 || 2.0.0-beta.2" } }, "sha512-mRyDmEw9/fCDXCGfL1sbgIw/GUK0PVxRW4YwgPN61F3LQ+fSyoBFarJVzgGyhF6kRzEQZ0FejDqm4WLXSeqQmw=="], + + "@bruits/satteri-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="], + + "@bruits/satteri-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.2.6", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1 || 2.0.0-beta.2", "@emnapi/runtime": "^1.7.1 || 2.0.0-beta.2" } }, "sha512-mRyDmEw9/fCDXCGfL1sbgIw/GUK0PVxRW4YwgPN61F3LQ+fSyoBFarJVzgGyhF6kRzEQZ0FejDqm4WLXSeqQmw=="], + + "@cloudflare/config/zod": ["zod@4.6.5", "", {}, "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q=="], + + "@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], + + "@poppinss/dumper/supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], + + "@vscode/emmet-helper/jsonc-parser": ["jsonc-parser@2.3.1", "", {}, "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg=="], + + "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + + "cf/@cloudflare/build-output-utils": ["@cloudflare/build-output-utils@0.9.4", "", { "dependencies": { "@cloudflare/config": "0.28.0" } }, "sha512-5rVdn81HEP2Uugs/wSfXu5XbadaZQkgKZl1xwkKb3kgatSOpTH2vszBXWTRh4IlE1vHnc3LwHcnc2O4TcXVbuQ=="], + + "cf/@cloudflare/config": ["@cloudflare/config@0.28.0", "", { "dependencies": { "zod": "4.6.5" } }, "sha512-2SaZn5rzNSxAR5VQYIlMm8+zHj2EGfLbaN+abYiPXDI42IFRCHSENpDxjAQpLj2NZnUVszHY4h6T1XljwHk5eQ=="], + + "cliui/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "csso/css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="], + + "httpsnippet/yargs": ["yargs@17.7.3", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g=="], + + "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + + "unifont/undici": ["undici@8.12.3", "", {}, "sha512-+fdndUTwBP86cVjihTp21hZHo7d6oj1c/47XPA3lL8mM0XHs+C4EkcjmgRsFnFk2f/UONGDy9ItBItmXsLOQHw=="], + + "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "yaml-language-server/request-light": ["request-light@0.5.8", "", {}, "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg=="], + + "yaml-language-server/yaml": ["yaml@2.8.3", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg=="], + + "youch/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], + + "@bruits/satteri-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="], + + "cf/@cloudflare/config/zod": ["zod@4.6.5", "", {}, "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q=="], + + "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], + + "httpsnippet/yargs/cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + + "httpsnippet/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "httpsnippet/yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + + "httpsnippet/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "httpsnippet/yargs/cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "httpsnippet/yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "httpsnippet/yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "httpsnippet/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "httpsnippet/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + } +} diff --git a/cloudflare.config.ts b/cloudflare.config.ts new file mode 100644 index 0000000..4d8b6e3 --- /dev/null +++ b/cloudflare.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "cf/config" + +export default defineConfig({ + accountId: "bf288a8d0e450c22157076593bc1f6be", + worker: { + name: "chek-docs", + compatibilityDate: "2026-10-08", + domains: ["docs.chek.page"], + }, +}) diff --git a/package.json b/package.json new file mode 100644 index 0000000..80ec7d9 --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "chek-docs", + "private": true, + "type": "module", + "scripts": { + "dev": "bun --bun astro dev", + "build": "bun --bun astro build && bun scripts/cloudflare.ts", + "check": "bun --bun astro check", + "deploy": "bun run build && cf deploy --prebuilt", + "lint": "oxlint --deny-warnings .", + "format": "oxfmt --write .", + "format:check": "oxfmt --check ." + }, + "dependencies": { + "@astrojs/markdown-satteri": "0.4.5", + "@astrojs/starlight": "0.42.7", + "astro": "7.4.2", + "sharp": "0.35.5", + "starlight-openapi": "0.27.0" + }, + "devDependencies": { + "@astrojs/check": "0.9.12", + "@cloudflare/build-output-utils": "0.9.3", + "@types/bun": "^1.4.4", + "cf": "^1.0.0-beta.13", + "oxfmt": "^0.73.0", + "oxlint": "^1.86.0", + "typescript": "^6" + }, + "packageManager": "bun@1.4.3" +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9fcb93a1c39254eef868780994381d0af45fd13a GIT binary patch literal 14510 zcmeI2e~esJ6~~9@AO2-fi=rT|O0|ec5v|fHU@6#^N`>-EqV-pzR>Ww~SQ8QuEg}9u z!D2-X2wLPXW1svRby{qM62g}YW1 z3aj)Jk}<(4_Fom@J@xVg^vnLDjc2=|!REKPX!BbIzTC9ZML@QVzMe8yy5gm0xZ=g9 zyW%CUwKCf91_xx@z=<}BXE%>4-smb{*y_f2e$0)3^(HIJA6@5wY#TU{a8r8e2LFj& zUv$&X8N@R)?x^b3J>N8-PFv`o*mX;yALF`ml`F1Y;p%(uPiUKs6t8;UZ4L-@vF`&H z8fcAv-D$4=(DxGhnOQe+=jUDXpMP_;hrVk-owlF@xJA*l=~uYgBR_D{)3F=>%12!3 zhBdBo^HoLz9d+8qZ@tlQz%8!J$Y1*^SAXosu30=NJKSP=)E||O?|+Q}b;dv+IN%mV z)0Qv&e|}GUoYT`*AOD)>LI7>-+o*%vp?}tX8bf^asL@n^a+d>)UTv}ZjDGL#w*9o8 ziXS+`jeq%i*NP6hR<+_I27cp~7hOKqh7amDkZl8}n>O&FJqru9hyEp$_=z#W zrGnH?$Z;&IpICyv9X22F>0E>VkM`g^F+Ykg?gsp;+JarI-?#O`%{`lP&PeT5JFDbN z@n>^E?eBZFe88IxsN;9%;z6r+oqM^yA&engu0K;}+jjG(HThJ7hrjP?0KXr(*VTS{ zw=2ny0o1LHzAT=89KH`t(gZS7nSK7APfckv6^wsu!OJ}R> zz}cFW8S3~)`BcW!Dj#<7>#2{O0IOx`3BT!i|NbaQiq3lwMvJL zxA|>k1-YT5P5!)e-Mj2OG5MTL@CB&H;;-}0_*KW-@>Mr{`oQU?L%z;0c-xsVpM0H3 z;C9GAip*KFaeRD6@xrWdy5y^kK5Pwc3{B8G$mi_i%o;?#Kbu<)G8rST6cU$&e+Az89VWycnG_=1;N926fv?pigC|LS)bP^XO;37pD~ zD;@fp4$Q}5#_!|8)m){I-vnQ)I!X4A@=#mS0u@$1-IK9-o;=G)^yW3=L7yapM6*niqn#l7e%j{S7vhg|+h{&U^D$Zjqct#$ zH9&3(u=tZ0v>#Ax7-U<#*AI=y3GxE{QcCG+_i6eL58tKzl+gyo$w98_{m`U1$Pz3n zwhl6_RKAtFp4&d{j{M;_ZtAa32>#?t>OJ6@Kf1>Lr(N~ix28H%r)^4oJ!w)t32$T} z)9#FV-92{bm=h0>e^AbpoCLXoL4fZ;(vF4LvsULx2zujG!I+!c4c zgUTJ2{+>_m;mqA}p1$4fLH~a2Mh=I$4>s&qe`tE;p}P2R?}07t&Zt|@yx;xmj}BU+ zHb+61e)Jcv(RAnLg6@>5{g+AqkiRb-a*gMn(KxieO>VMod#``?rF0W6G%eBEIPeKs z-IrT`Z=1%8`(5e!HJy7syt!cByZ5i}tLcpa{sG#?-}m+2W6NcK?k=>a8!tX*cLbJu z+j5Tb^ryMW-|n?O{N5b<`*k+?yGIQ-f`(k&*7G!m{eBF7ewNGrsol~2JiXN+M$la+ zc|2kw{U$%jSyg-RJE{GtTdwojr+l7OxX?f=nx11Aw*9qN6T558=x(M+UJ(1;bdlZ3 z6xYAr-U*eiKHqYq^~d(;EsXAO1=Q`HMt!^`D(cR({F#lSU7L(Uys$^QmhcP1)Zfn3 zS=ac-^UCc?hmh0U`gYg&*9&$JLavqfD8yad>(+jDx87I1&VV{?7Ly65bj@nfM6Pk* z1s92j@zc1l3o@p;*YNixL-C(}-|;rc?xReW?4|w7oB);wXI#X}%G=ud^8VTCeUhJ&l9Z)!5sB@;M zFYNK;KHB>N?O%9uPSqs?dxW=RL$SZ~OYSVRhMxb6YxeTn{yeDNe~0N2ZPB}lpug%C zkLFbDEqt|FYZmhJL`IvV)M3$IDBa9KTqH8cI=jO{@R}H#?SkGvh45W&WRuJDe6ntN1ooH zeM$QB{iW}h2X*((UR{LTq3AE!7Kd2;pYy&b__%E8zum{b{_$kp+FiT$R`4D3m8}1~ z^~3s~k2dWien`#+bRdo$==+zk=&USmILjS*;&B@T?`6z4hx_Ne^4M|1xBVO9XYFO% zzv@fxyYuK3J0J^uWZRo=&h4yy`>Fdl03XgPtg00cxuw#zNPVx z-oeqv+mD_!ftSBeZ~o_i$+Yt$#2x+U+>OH+boTSEn)B4|!nr$N2)JA4-LZd1NPQ4m zWSLCjj>XtMI}YeMN6{t3o&A)&dk%c4_d^qKkcG^p>K^1 diff --git a/public/fonts/OFL-BricolageGrotesque.txt b/public/fonts/OFL-BricolageGrotesque.txt new file mode 100644 index 0000000..4dfd4fe --- /dev/null +++ b/public/fonts/OFL-BricolageGrotesque.txt @@ -0,0 +1,93 @@ +Copyright 2022 The Bricolage Grotesque Project Authors (https://github.com/ateliertriay/bricolage) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/public/fonts/OFL-DMSans.txt b/public/fonts/OFL-DMSans.txt new file mode 100644 index 0000000..a0eedfb --- /dev/null +++ b/public/fonts/OFL-DMSans.txt @@ -0,0 +1,93 @@ +Copyright 2014 The DM Sans Project Authors (https://github.com/googlefonts/dm-fonts) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/public/fonts/bricolage-700-593306227576.woff2 b/public/fonts/bricolage-700-593306227576.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..1dee877a3bddb24cf8c1dd3e2982096f7c8988df GIT binary patch literal 34348 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 `${path}\n Cache-Control: public, max-age=31536000, immutable\n`) + .join("\n"), +) + +await cleanBuildOutputDir(root) +await writeRootConfig(root, { accountId: config.accountId }, { isPreview: false, mode: undefined }) +await writeWorkerConfig({ root, config: config.worker }) +await writeAssets({ root, sourceDirectory: dist }) diff --git a/src/assets/mark.svg b/src/assets/mark.svg new file mode 100644 index 0000000..b809f4a --- /dev/null +++ b/src/assets/mark.svg @@ -0,0 +1 @@ + diff --git a/src/content.config.ts b/src/content.config.ts new file mode 100644 index 0000000..d614714 --- /dev/null +++ b/src/content.config.ts @@ -0,0 +1,7 @@ +import { defineCollection } from "astro:content" +import { docsLoader } from "@astrojs/starlight/loaders" +import { docsSchema } from "@astrojs/starlight/schema" + +export const collections = { + docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), +} diff --git a/src/content/docs/api/overview.md b/src/content/docs/api/overview.md new file mode 100644 index 0000000..bae1810 --- /dev/null +++ b/src/content/docs/api/overview.md @@ -0,0 +1,73 @@ +--- +title: API overview +description: How to sign in, page through lists and handle errors in Chek server's HTTP API. +--- + +Chek server has a JSON API under `/api/v1`. The [Chek app](https://apps.apple.com/us/app/chek-books-comics/id6761271144) and web app use it, and you can build on it too: your own client, a script that adds libraries, a bot that tells you about new chapters. + +The API is described by a hand-written OpenAPI 3.1 file, [`openapi.yaml`](https://github.com/readchek/chek/blob/main/openapi.yaml), which is the contract: the server changes in step with it, and each server release tags the version it implements. The [reference](/api/reference/) on this site is built from the file at the release these docs describe. To generate a client, use the file from the tag that matches your server. + +## Authentication + +Every operation needs a session token, except `GET /health`, `POST /api/v1/setup` and signing in. + +```sh +curl -X POST $SERVER/api/v1/auth/sign-in \ + -H 'content-type: application/json' \ + -d '{"name": "sam", "password": "", "device": "My script"}' +``` + +```json +{ "token": "…", "user": { "id": 2, "name": "sam", "role": "reader", "libraryIds": [1] } } +``` + +Send the token on every request: + +```http +Authorization: Bearer +``` + +- `device` labels the session in the person's list of signed-in devices. Name your client. +- Sessions last 90 days after their last use. `POST /api/v1/auth/sign-out` ends one. +- HTTP Basic credentials work too, for clients that can't keep a token, such as OPDS readers. Prefer tokens: they can be signed out one at a time, and a password change doesn't break other devices. +- For [sign-in providers](/self-hosting/sign-in-providers/), list them with `GET /api/v1/auth/providers`, then follow the PKCE flow in the reference under `authorize` and `exchange`. + +## Access + +Admins see every library. Readers see only the libraries they were granted, and everything else answers `404`, as if it didn't exist. Admin-only operations answer `403` for readers. + +## Browsers + +Browsers may call the API only from the origins in [`CHEK_WEB_ORIGINS`](/self-hosting/configuration/#behind-a-proxy), `https://app.chek.page` by default. Requests carry a bearer token and never cookies. A page served over HTTPS can only call servers on HTTPS. + +## Lists + +Lists answer `{ "items": [...] }`. Long lists are paged with an opaque cursor: + +```sh +curl "$SERVER/api/v1/libraries/1/publications?limit=100" -H "Authorization: Bearer $TOKEN" +``` + +```json +{ "items": [ ... ], "nextCursor": "…" } +``` + +Pass `nextCursor` back as `cursor` for the next page. It's absent on the last page. `limit` is 50 by default and clamped to 1 through 200. Search pages by position instead, since relevance has no stable key. + +## Errors + +Errors answer with an HTTP status and a body you can switch on: + +```json +{ "code": "progress_conflict", "message": "Another device saved a newer position." } +``` + +Switch on `code`, not `message`. Messages explain the problem to a developer and may change. Each operation in the [reference](/api/reference/) lists the codes it can answer, such as `content_changed` when a file changed on disk since its last scan, or `too_many_attempts` after too many wrong passwords. + +## Caching + +Files, pages and covers carry an `ETag`. Send it back as `If-None-Match` and an unchanged one answers `304`. Files support `Range` requests where the library can serve them. + +## OPDS + +For reading apps rather than custom clients, the server also speaks [OPDS 2](/self-hosting/opds/) under `/opds/v2`. diff --git a/src/content/docs/api/reading.md b/src/content/docs/api/reading.md new file mode 100644 index 0000000..055bc8b --- /dev/null +++ b/src/content/docs/api/reading.md @@ -0,0 +1,88 @@ +--- +title: Read and sync +description: Walk a library, fetch pages and files, and keep a reading position in step across devices. +--- + +This walks through what a reading client does, using `curl`. Every request sends `Authorization: Bearer $TOKEN`; it's left out below for brevity. + +## Find something to read + +```sh +GET /api/v1/libraries # the libraries you can open +GET /api/v1/libraries/{libraryId}/series # a library's series, in title order +GET /api/v1/series/{seriesId}/publications # a series' publications, in reading order +GET /api/v1/me/continue-reading # started and not finished, newest first +GET /api/v1/recently-added # newest first +GET /api/v1/search?q=saga # titles, series, contributors and publishers +``` + +Each publication has a `format`. Comic archives (`cbz`, `cbr`, `cb7`, `cbt`) and extension chapters (`remote`) are read a page at a time. Everything else is read as a whole file. Pick how to open a publication from `format`, never from a URL or file name. + +`available: false` means the file has gone from disk or its source. It keeps its reading position in case it comes back. + +## Comics: pages + +```sh +GET /api/v1/publications/{publicationId}/pages +``` + +```json +{ "items": [{ "index": 0, "mediaType": "image/jpeg", "size": 412331 }, ...] } +``` + +Then fetch each page by its zero-based index. The bytes are the original image, unchanged. + +```sh +GET /api/v1/publications/{publicationId}/pages/0 +``` + +## Books: the file + +```sh +GET /api/v1/publications/{publicationId}/file +``` + +The file is sent as it is on disk, with `Range` support, so a reader can open a large EPUB or PDF without downloading all of it first. Keep the `ETag` and send it as `If-None-Match` to check a downloaded copy is still current. + +Covers come from `GET /api/v1/publications/{publicationId}/cover` as JPEG thumbnails up to 400 by 600 pixels. `hasCover` says whether there is one. + +## Positions + +A reading position is a [Readium Locator](https://readium.org/architecture/models/locators/), stored as you send it, up to 16 KiB. [Chek Reader](https://github.com/readchek/chek-reader) and Readium's toolkits produce them as they are. + +```sh +GET /api/v1/publications/{publicationId}/progress +``` + +```json +{ "revision": 4, "locator": { "href": "chapter3.xhtml", "type": "application/xhtml+xml", "locations": { "totalProgression": 0.31 } }, "device": "iPad", "updatedAt": "2026-10-25T09:12:00Z" } +``` + +Revision `0` with a `null` locator means nothing is saved yet. + +To save, send the revision you last read: + +```sh +PUT /api/v1/publications/{publicationId}/progress +{ "expectedRevision": 4, "locator": { ... } } +``` + +If another device saved since, the answer is `409 progress_conflict` with that device's position in `current`: + +```json +{ "code": "progress_conflict", "message": "…", "current": { "revision": 5, "locator": { ... }, "device": "iPhone" } } +``` + +Ask the reader which position to keep, then save again with `expectedRevision` set to `current.revision`. The server never decides for them, because going back to reread something is as deliberate as reading on. + +A `totalProgression` of 1 marks a publication finished, which takes it off Continue reading. + +## Bookmarks and highlights + +```sh +GET /api/v1/publications/{publicationId}/annotations +POST /api/v1/publications/{publicationId}/annotations +{ "kind": "highlight", "locator": { ..., "text": { "highlight": "the words" } }, "color": "yellow", "note": "…" } +``` + +Change one with `PATCH /api/v1/annotations/{annotationId}` and remove it with `DELETE`. The last save wins. Fetch the whole list when opening a publication, so deletions made on another device show up. A publication holds up to 5,000 annotations per person. diff --git a/src/content/docs/get-started/introduction.md b/src/content/docs/get-started/introduction.md new file mode 100644 index 0000000..40538d6 --- /dev/null +++ b/src/content/docs/get-started/introduction.md @@ -0,0 +1,24 @@ +--- +title: What Chek is +description: The app, the server, the extensions and the reading engine, and how they fit together. +--- + +Chek is a reader for books and comics wherever they're kept. It's made of a few pieces, and you only need the ones that fit what you're doing. + +| Piece | What it is | Where it lives | +| --- | --- | --- | +| **Chek app** | The reader for iPhone and iPad, free on the [App Store](https://apps.apple.com/us/app/chek-books-comics/id6761271144). It reads your own files with no account, and connects to a Chek server. | [readchek/app](https://github.com/readchek/app) | +| **Chek web app** | Account and content settings at [app.chek.page](https://app.chek.page), talking to your server from the browser. | [readchek/app](https://github.com/readchek/app/tree/main/web) | +| **Chek server** | An open-source, self-hosted library server written in Rust. It scans folders, reads Komga and Kavita libraries, runs source extensions, syncs reading positions and speaks OPDS 2. | [readchek/chek](https://github.com/readchek/chek) | +| **Extensions** | Sources the server reads series from, built as WebAssembly components and installed from extension repositories. | [readchek/extensions](https://github.com/readchek/extensions) | +| **Chek Reader** | The Swift package that opens, lays out and pages publications in the app. It has no Chek-specific code, so any Apple app can use it. | [readchek/chek-reader](https://github.com/readchek/chek-reader) | + +The server is optional. The app reads files on the device without it. A server adds a library that follows you between devices, positions and highlights that sync, and sources from Komga, Kavita and extensions. + +## Pick your path + +- **I want my own library server.** Start with the [quick start](/get-started/quick-start/), then [install it properly](/self-hosting/install/) behind HTTPS. +- **I want to build a client, script or integration.** Read the [API overview](/api/overview/) and the [API reference](/api/reference/). +- **I want my OPDS reader to browse Chek.** See [OPDS](/self-hosting/opds/). +- **I want to write a source.** The [extension guide](https://github.com/readchek/chek/blob/main/extensions/README.md) covers `chek ext` until its pages land here. +- **I want a reader in my own Swift app.** See [Chek Reader's README](https://github.com/readchek/chek-reader#readme) until its pages land here. diff --git a/src/content/docs/get-started/quick-start.md b/src/content/docs/get-started/quick-start.md new file mode 100644 index 0000000..51ea849 --- /dev/null +++ b/src/content/docs/get-started/quick-start.md @@ -0,0 +1,74 @@ +--- +title: Quick start +description: Run Chek server with Docker, create the admin, add a folder and read it in the app. +--- + +This gets a server running on your own machine in a few minutes. It's good for trying Chek. Before opening it to other devices outside your home network, follow [Install](/self-hosting/install/) and [HTTPS and reverse proxies](/self-hosting/reverse-proxy/). + +You need [Docker](https://docs.docker.com/get-docker/) and a folder of books or comics. + +## 1. Start the server + +```sh +docker run -d --name chek -p 8080:8080 \ + -v chek-data:/data \ + -v /path/to/comics:/library:ro \ + ghcr.io/readchek/chek:latest +``` + +- `chek-data` holds the database, covers and installed extensions. Keep it between upgrades. +- Your folder is mounted read-only at `/library`. The server never changes your files. + +Check it's up: + +```sh +curl localhost:8080/health +``` + +## 2. Create the admin + +A new server has no accounts. It prints a one-time setup token in its log so that only someone who can read the log can claim it: + +```sh +docker logs chek +``` + +Trade the token for the first admin account: + +```sh +curl -X POST localhost:8080/api/v1/setup \ + -H 'content-type: application/json' \ + -d '{"setupToken": "", "name": "admin", "password": ""}' +``` + +The answer holds a session `token`. Keep it for the next step: + +```sh +TOKEN= +``` + +## 3. Add your folder + +```sh +curl -X POST localhost:8080/api/v1/libraries \ + -H "Authorization: Bearer $TOKEN" \ + -H 'content-type: application/json' \ + -d '{"name": "Comics", "path": "/library"}' +``` + +The server starts scanning at once. Large folders take a while; the library's `scan` field shows how far it's got: + +```sh +curl localhost:8080/api/v1/libraries -H "Authorization: Bearer $TOKEN" +``` + +## 4. Read + +- **In the Chek app**, add a server with your computer's address on your network, such as `192.168.1.20:8080`, and sign in as `admin`. The app uses plain HTTP only for addresses on your local network; anywhere else it needs HTTPS. +- **In an OPDS 2 reader** such as Thorium or KOReader, add `http://:8080/opds/v2/catalog` and sign in with the same name and password. + +## Next + +- [Install](/self-hosting/install/) covers Compose, Dokploy and the image tags. +- [First steps](/self-hosting/first-steps/) adds readers and more libraries. +- [Configuration](/self-hosting/configuration/) lists every setting. diff --git a/src/content/docs/index.mdx b/src/content/docs/index.mdx new file mode 100644 index 0000000..f01e719 --- /dev/null +++ b/src/content/docs/index.mdx @@ -0,0 +1,35 @@ +--- +title: Chek Docs +description: Run your own Chek server, build on its API, write source extensions and embed Chek Reader. +template: splash +hero: + tagline: Run your own Chek server, build on its API, write sources for it, and read with the same engine as the app. + actions: + - text: Run a server + link: /get-started/quick-start/ + icon: right-arrow + - text: API reference + link: /api/reference/ + variant: minimal +--- + +import { Card, CardGrid } from "@astrojs/starlight/components" + + + + Run Chek server with Docker, point it at your folders, Komga or Kavita, and read from the app on + every device. [Install the server](/self-hosting/install/). + + + Sign in, browse libraries, stream pages and sync reading positions over HTTP. + [API overview](/api/overview/). + + + Sources are WebAssembly components the server runs in a sandbox. Guides are coming; for now, see + the [extension README](https://github.com/readchek/chek/blob/main/extensions/README.md). + + + The Swift package behind the app reads EPUB, PDF, comics and more in any Apple app. Guides are + coming; for now, see [its README](https://github.com/readchek/chek-reader#readme). + + diff --git a/src/content/docs/self-hosting/configuration.md b/src/content/docs/self-hosting/configuration.md new file mode 100644 index 0000000..a343293 --- /dev/null +++ b/src/content/docs/self-hosting/configuration.md @@ -0,0 +1,62 @@ +--- +title: Configuration +description: Every environment variable Chek server reads. +--- + +Chek server is configured only with environment variables, and all of them are optional. A value it can't read stops the server at startup with the variable's name and the reason, so a typo never runs with a default you didn't mean. + +## Basics + +| Variable | Default | What it does | +| --- | --- | --- | +| `CHEK_BIND` | `127.0.0.1:8080`, or `0.0.0.0:8080` in the image | Address and port to listen on | +| `CHEK_DATA_DIR` | `./data`, or `/data` in the image | Where the database, covers and extensions live | +| `CHEK_SCAN_WORKERS` | cores − 1 | How many files a scan reads at once. Scanning on every core slows down pages for readers | +| `CHEK_SETUP_TOKEN` | logged at start | Your own [setup token](/self-hosting/first-steps/#create-the-admin), 32 characters or more, so you don't need the log | +| `CHEK_MAX_UPLOAD_BYTES` | `2147483648` (2 GiB) | Largest file an admin can [upload](/self-hosting/uploads/) | + +## Behind a proxy + +See [HTTPS and reverse proxies](/self-hosting/reverse-proxy/). + +| Variable | Default | What it does | +| --- | --- | --- | +| `CHEK_TRUSTED_PROXIES` | none | Your proxy's addresses or CIDR ranges, comma-separated, such as `10.0.0.0/8`. Client addresses are read from `X-Forwarded-For` only on requests from these | +| `CHEK_CLIENT_IP_HEADER` | none | A header to read the client address from instead, such as `CF-Connecting-IP` behind Cloudflare | +| `CHEK_WEB_ORIGINS` | `https://app.chek.page` | Comma-separated origins allowed to call the API from a browser. Add yours if you host a web client | + +## Sign-in + +See [Sign-in providers](/self-hosting/sign-in-providers/). + +| Variable | Default | What it does | +| --- | --- | --- | +| `CHEK_PUBLIC_URL` | none | The address people reach the server at. Needed for sign-in providers | +| `CHEK_OIDC_PROVIDERS` | none | Comma-separated provider ids, such as `authentik,google` | +| `CHEK_OIDC__*` | | Each provider's settings | +| `CHEK_OIDC_REDIRECTS` | `chek://` | Comma-separated prefixes, besides the public URL, that apps may ask to be sent back to | +| `CHEK_DEMO_USERS` | none | Comma-separated account names to treat as shared [demo accounts](/self-hosting/users/#demo-accounts) | + +## Sources and extensions + +See [Komga and Kavita](/self-hosting/komga-and-kavita/) and [Extensions](/self-hosting/extensions/). + +| Variable | Default | What it does | +| --- | --- | --- | +| `CHEK_SOURCE_SYNC_MINUTES` | `60` | How often Komga, Kavita and extension libraries sync. `0` turns the timer off | +| `CHEK_EXTENSION_IMAGE_CACHE_MB` | `2048` | Disk cache for covers and pages from extensions. `0` turns it off | +| `CHEK_BROWSER_SOLVER_URL` | none | A FlareSolverr-compatible service, for sites behind a challenge | +| `CHEK_BROWSER_EXECUTABLE` | none, set in `-browser` images | Absolute path to Chromium, for extensions that drive interactive pages | +| `CHEK_BROWSER_NATIVE` | `false`, `true` in `-browser` images | Lets Chromium make its own connections through a restricted proxy | +| `CHEK_BROWSER_ARGS` | `["--headless=new"]` | Chromium launch arguments, as a JSON array | +| `CHEK_OUTBOUND_PROXY_URL` | none | An HTTP CONNECT proxy for all extension traffic | +| `CHEK_OUTBOUND_PROXY_USERNAME`, `CHEK_OUTBOUND_PROXY_PASSWORD` | none | The proxy's credentials, set together | + +## Logs + +See [Logs and tracing](/self-hosting/logs/). + +| Variable | What it does | +| --- | --- | +| `RUST_LOG` | Log level, such as `info` or `debug` | +| `OTEL_EXPORTER_OTLP_ENDPOINT` | Sends logs and traces to an OpenTelemetry collector over OTLP/HTTP. The standard `OTEL_*` variables apply | diff --git a/src/content/docs/self-hosting/extensions.md b/src/content/docs/self-hosting/extensions.md new file mode 100644 index 0000000..bef58d2 --- /dev/null +++ b/src/content/docs/self-hosting/extensions.md @@ -0,0 +1,68 @@ +--- +title: Extensions on your server +description: Repositories, installs, extension libraries, content ratings and sites behind a challenge. +--- + +Extensions are sources the server reads series from, such as a webcomic's site or a public-domain library. Each one is a WebAssembly component that runs in a sandbox inside the server, with no files and no network except the sites it declares. This page covers running them. Writing one is covered in the [extension guide](https://github.com/readchek/chek/blob/main/extensions/README.md) until its pages land here. + +## Repositories + +Extensions are installed from repositories: an `index.json` and the components it lists, served over HTTPS. + +- **Server-wide repositories** are the admin's allow list, and everyone sees them. A new server starts with the official one, `https://chek.page/extensions/index.json`, which an admin can remove. +- **Personal repositories** are added by people other than admins, and only they see them. This is off until an admin turns on `allowAnyRepository`: + + ```sh + curl -X PUT $SERVER/api/v1/settings \ + -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ + -d '{"allowAnyRepository": true}' + ``` + + Turning it off again keeps those repositories but nothing is installed or run from them. + +The server reads every index again every six hours, so new versions show up as updates. Private repositories take a bearer token, a username and password, or a header, which are sent only to the repository's own origin and never returned by the API. + +The Chek app manages repositories and installs, so most people never call these endpoints themselves. + +## Installs + +Everyone installs extensions for themselves. Installing downloads the component, checks its SHA-256 digest, and checks that it declares the same id, version, sites and capabilities as the index, so a repository can't list one thing and ship another. Components are kept in the data folder under `extensions/`, shared by everyone on the same version. + +## Extension libraries + +An admin can turn an extension they installed into a library that readers are granted like any other. It starts empty and holds only the series an admin follows into it, synced on the same schedule as [Komga and Kavita](/self-hosting/komga-and-kavita/#syncing). It runs on that admin's install, so readers don't need the extension. If the admin uninstalls it, the library answers `source_unavailable` until it's installed again. + +## Content ratings + +Each person chooses which content ratings they see, from Safe and Suggestive through Erotica and Pornographic. Search passes the choice to the extension and drops results rated outside it, so a source that ignores the choice still can't show them. Until someone chooses, and always on demo accounts, they see Safe and Suggestive. + +The iOS app only narrows the choice. Erotica and Pornographic are turned on in the web app at [app.chek.page](https://app.chek.page), after the person confirms they're an adult. + +## Images + +Covers and pages from extensions are fetched by the server, never by the app, and kept on disk in `extensions/images` in the data folder. `CHEK_EXTENSION_IMAGE_CACHE_MB` caps it, 2048 by default; the least recently read images go first. `0` turns the cache off. + +## Sites behind a challenge + +Some sites put a browser challenge in front of their pages. Extensions that declare the `browser` capability can get past one in two ways, and both are the admin's to set up. + +- **A solver.** Point `CHEK_BROWSER_SOLVER_URL` at a [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr)-compatible service, such as `http://flaresolverr:8191/v1`. When a request meets a challenge, the server has the solver load the page, keeps the cookies it earned and retries once. +- **A browser in the server.** The `-browser` images include Chromium, for extensions that drive interactive pages. Give the container `shm_size: 1gb`. Chromium's own sandbox is turned off for container compatibility, so run only extensions you trust with these images. + +A signed-in person can open `/verify` on the server to complete a site's checkbox challenge themselves for an installed source. The cookies are kept in the data folder until the site expires them. + +## Outbound proxy + +To send all extension traffic through one HTTP CONNECT proxy: + +```sh +CHEK_OUTBOUND_PROXY_URL=http://proxy.internal:8888 +CHEK_OUTBOUND_PROXY_USERNAME= +CHEK_OUTBOUND_PROXY_PASSWORD= +``` + +- Only HTTP CONNECT proxies work, not SOCKS or HTTPS proxy endpoints. Keep credentials in their own variables, not in the URL. +- It covers source requests, images and browser traffic. Repository downloads, sign-in providers and telemetry connect directly. +- If the proxy fails, requests fail. They never fall back to a direct connection. +- Run the proxy where it can't reach your private network, since it resolves the names sources ask for. +- Without it, sources connect directly and ignore `HTTP_PROXY` and `HTTPS_PROXY`. diff --git a/src/content/docs/self-hosting/first-steps.md b/src/content/docs/self-hosting/first-steps.md new file mode 100644 index 0000000..300c05f --- /dev/null +++ b/src/content/docs/self-hosting/first-steps.md @@ -0,0 +1,62 @@ +--- +title: First steps +description: Claim a new server, add libraries and give people their own accounts. +--- + +Libraries and accounts are managed through the API for now, so these steps use `curl`. Every request after setup sends the admin's session token as `Authorization: Bearer `. + +```sh +SERVER=https://chek.example.org # or http://localhost:8080 +``` + +## Create the admin + +A server with no accounts accepts one setup request. The token is `CHEK_SETUP_TOKEN` if you set it, or the one-time token in the log otherwise (`docker logs chek`). + +```sh +curl -X POST $SERVER/api/v1/setup \ + -H 'content-type: application/json' \ + -d '{"setupToken": "", "name": "admin", "password": ""}' +``` + +The answer is a session. Save its `token`: + +```sh +TOKEN= +``` + +Once an admin exists, setup answers `409 already_set_up`. To sign in again later, use `POST /api/v1/auth/sign-in` with the same name and password. + +## Add libraries + +A library is a folder on the server, a [Komga or Kavita server](/self-hosting/komga-and-kavita/), or a source run by an [extension](/self-hosting/extensions/). For a folder, give the path inside the container: + +```sh +curl -X POST $SERVER/api/v1/libraries \ + -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ + -d '{"name": "Books", "path": "/library/books"}' +``` + +The first scan starts at once. Every folder library is scanned again when the server starts, skipping files whose size and modification time haven't changed. To pick up changes while it runs, ask for a scan: + +```sh +curl -X POST $SERVER/api/v1/libraries/1/scan -H "Authorization: Bearer $TOKEN" +``` + +## Add readers + +There are two roles. **Admins** manage libraries and accounts and see every library. **Readers** see only the libraries they were granted; anything else answers as not found. + +```sh +curl -X POST $SERVER/api/v1/users \ + -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ + -d '{"name": "sam", "password": "", "role": "reader", "libraryIds": [1, 2]}' +``` + +Change someone's role, password or libraries with `PATCH /api/v1/users/{userId}`. See [Users and access](/self-hosting/users/) for sessions, sign-in limits and demo accounts. + +## Connect + +- **Chek app:** add the server's address and sign in. The app reads your libraries, syncs your place, and manages extension repositories and installs. +- **OPDS readers:** see [OPDS](/self-hosting/opds/). +- **Your own code:** see the [API overview](/api/overview/). diff --git a/src/content/docs/self-hosting/install.md b/src/content/docs/self-hosting/install.md new file mode 100644 index 0000000..400bb34 --- /dev/null +++ b/src/content/docs/self-hosting/install.md @@ -0,0 +1,94 @@ +--- +title: Install +description: Run Chek server with Docker, Docker Compose or Dokploy, or build it from source. +--- + +Chek server runs as a single process with an SQLite database, so one container and one volume is a whole install. + +## Images + +CI publishes `ghcr.io/readchek/chek` for amd64 and arm64. + +| Tag | What you get | +| --- | --- | +| `latest` | The newest release | +| A version, such as `0.3.0` | That release, for pinning | +| `edge` | The newest commit on `main` | +| Any of the above with `-browser`, such as `latest-browser` | The same server with Chromium, for [browser-backed extensions](/self-hosting/extensions/#sites-behind-a-challenge) | + +The plain image is small and has no browser. Use a `-browser` tag only if you run extensions that need one, and only extensions you trust. + +In the image, the server listens on `0.0.0.0:8080`, keeps its data in `/data` and runs as an unprivileged `chek` user (uid 10001). + +## Docker + +```sh +docker run -d --name chek --restart unless-stopped -p 8080:8080 \ + -e CHEK_SETUP_TOKEN=<32 or more random characters> \ + -v chek-data:/data \ + -v /srv/books:/library/books:ro \ + -v /srv/comics:/library/comics:ro \ + ghcr.io/readchek/chek:latest +``` + +Mount each folder you want to read under `/library`, read-only unless you want admins to [upload](/self-hosting/uploads/) into it. Setting `CHEK_SETUP_TOKEN` saves reading it from the log on first start. + +## Docker Compose + +```yaml title="compose.yaml" +services: + chek: + image: ghcr.io/readchek/chek:latest + restart: unless-stopped + ports: ["8080:8080"] + environment: + CHEK_SETUP_TOKEN: ${CHEK_SETUP_TOKEN:?set CHEK_SETUP_TOKEN} + volumes: + - chek-data:/data + - /srv/books:/library/books:ro + - /srv/comics:/library/comics:ro + +volumes: + chek-data: +``` + +```sh +CHEK_SETUP_TOKEN=$(openssl rand -hex 24) docker compose up -d +``` + +Behind a reverse proxy on the same Docker network, replace `ports` with `expose: ["8080"]` and set [`CHEK_TRUSTED_PROXIES`](/self-hosting/reverse-proxy/). + +For browser-backed extensions, use a `-browser` tag and give the container more shared memory for Chromium: + +```yaml + image: ghcr.io/readchek/chek:latest-browser + shm_size: 1gb +``` + +## Dokploy + +The chek repository has a Compose file ready for Dokploy at [`deploy/server/compose.yaml`](https://github.com/readchek/chek/blob/main/deploy/server/compose.yaml). It runs the `edge-browser` image behind Dokploy's proxy. + +1. Create a **Compose** application from `readchek/chek` and select `deploy/server/compose.yaml`. +2. In **Environment**, set `CHEK_SETUP_TOKEN` to 32 or more random characters. To pin a release instead of following `main`, set `CHEK_IMAGE`, such as `ghcr.io/readchek/chek:0.3.0-browser`. +3. Route your domain to service `chek`, port `8080`, with HTTPS. +4. Deploy, then [create the admin](/self-hosting/first-steps/). + +That file mounts no book folders. To read local files, add a read-only bind mount such as `/srv/books:/library/books:ro`. + +## From source + +You need a recent stable Rust, [libvips](https://www.libvips.org) for cover thumbnails and [libarchive](https://www.libarchive.org) for RAR and 7-Zip comics. + +```sh +apt install libvips-dev libarchive-dev # Debian or Ubuntu +brew install vips libarchive # macOS + +git clone https://github.com/readchek/chek +cd chek +cargo run --release --bin chek +``` + +Run from source, the server listens on `127.0.0.1:8080` and keeps its data in `./data`. Change both with [`CHEK_BIND` and `CHEK_DATA_DIR`](/self-hosting/configuration/). + +To build the image yourself, run `docker build -t chek .` in the repository, or `docker build --target browser -t chek-browser .` for the browser image. diff --git a/src/content/docs/self-hosting/komga-and-kavita.md b/src/content/docs/self-hosting/komga-and-kavita.md new file mode 100644 index 0000000..eb86ef2 --- /dev/null +++ b/src/content/docs/self-hosting/komga-and-kavita.md @@ -0,0 +1,60 @@ +--- +title: Komga and Kavita +description: Read a Komga or Kavita server's libraries through Chek, with progress written back. +--- + +An admin can add a [Komga](https://komga.org) or [Kavita](https://www.kavitareader.com) server as a library. Everyone granted that library in Chek reads what one Komga or Kavita account sees, with pages and files fetched through Chek, so Komga or Kavita keeps the only copy. + +Use an account made for Chek. Its credentials are checked before the library is added, stored on the server, and never returned by the API. + +## Komga + +The account needs Komga's **page streaming** and **file download** roles. Use an API key made in Komga for that account, or its email and password. + +```sh +curl -X POST $SERVER/api/v1/libraries \ + -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ + -d '{"name": "Komga", "komga": {"url": "http://komga:25600", "apiKey": ""}}' +``` + +| Field | What it does | +| --- | --- | +| `url` | Komga's address, including any path it's served under | +| `apiKey`, or `username` and `password` | The account to read with | +| `libraryIds` | Komga library ids to read. Leave it out for every library the account sees | +| `writeProgress` | `true` by default. Writes your reading position back to Komga | + +A sync reads the series and the books Komga has finished analyzing, with their titles, numbers, summaries, dates, authors, publisher, language and reading direction. Komga always sends whole files, even when asked for part of one. + +## Kavita + +The account needs Kavita's **download** role for files. Copy its API key from the account's settings page in Kavita. + +```sh +curl -X POST $SERVER/api/v1/libraries \ + -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ + -d '{"name": "Kavita", "kavita": {"url": "http://kavita:5000", "apiKey": ""}}' +``` + +The fields are the same as Komga's, except that Kavita takes only an API key and its `libraryIds` are numbers. + +Each Kavita chapter becomes one publication. A file that's a whole volume is called "Volume 1", a loose chapter "Chapter 12", and a special by its name, unless the file's own metadata gives a title. + +## Syncing + +Komga and Kavita can't tell Chek when something changes, so Chek syncs them: + +- when the server starts, +- every `CHEK_SOURCE_SYNC_MINUTES`, 60 by default, or never with `0`, +- and when an admin asks, with `POST /api/v1/libraries/{libraryId}/scan`. + +A book is read again only when it, its file or its series changed. Books that disappear are marked unavailable, not deleted, so reading positions survive if they come back. Progress, bookmarks, highlights and an admin's metadata edits are kept in Chek and survive syncs. + +## Progress written back + +The admin who adds the library has their progress written back to the Komga or Kavita account, so its own apps resume where Chek left off. Only that one person is written back, because everyone reading the library shares the one account. Send `"writeProgress": false` to turn it off. + +- **Komga:** EPUB positions go as Readium locators, and comics and PDFs as a page number. +- **Kavita:** comics and PDFs go as a page number. Kavita keeps a book's place in a form a Readium locator can't name, so a book is written back only once it's finished. + +Reaching the last page marks a book read. A write that Komga or Kavita refuses is logged, not retried. diff --git a/src/content/docs/self-hosting/logs.md b/src/content/docs/self-hosting/logs.md new file mode 100644 index 0000000..79c89b3 --- /dev/null +++ b/src/content/docs/self-hosting/logs.md @@ -0,0 +1,19 @@ +--- +title: Logs and tracing +description: Read the server's logs and send them to OpenTelemetry. +--- + +The server logs to standard output, so `docker logs chek` shows them. Set the level with `RUST_LOG`, such as `RUST_LOG=debug`. + +Every request is logged with its method, route, path, status, duration, client address and user agent, inside a span named after its route, such as `GET /api/v1/publications/{publicationId}/pages/{pageIndex}`, so traces group by endpoint. + +## OpenTelemetry + +To send logs and traces to an OpenTelemetry collector over OTLP/HTTP: + +```sh +OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318 +OTEL_SERVICE_NAME=chek +``` + +The standard `OTEL_*` variables apply. The exporter doesn't use TLS, since the collector is expected on the same private network. Queued data is sent when the server shuts down. diff --git a/src/content/docs/self-hosting/opds.md b/src/content/docs/self-hosting/opds.md new file mode 100644 index 0000000..fb99494 --- /dev/null +++ b/src/content/docs/self-hosting/opds.md @@ -0,0 +1,21 @@ +--- +title: OPDS +description: Browse and read Chek from OPDS 2 apps such as Thorium and KOReader. +--- + +Chek serves an [OPDS 2](https://drafts.opds.io/opds-2.0) catalog, so reading apps that speak OPDS can browse and download from it. + +``` +https://chek.example.org/opds/v2/catalog +``` + +Sign in with a Chek name and password. OPDS apps can't hold session tokens, so they send HTTP Basic credentials. When they don't, the catalog answers `401` with both a Basic challenge, which KOReader follows, and an [Authentication for OPDS](https://drafts.opds.io/authentication-for-opds-1.0) document, which Thorium needs. People who sign in only through a [provider](/self-hosting/sign-in-providers/) need to set a password first. + +## What's in it + +- **The catalog** leads with Continue reading and Recently added, then lists the libraries you may open. +- **A library** lists its series, after links to All publications and Recently added. +- **A series** lists its publications in reading order, 50 to a page. +- **Search** is offered for the whole catalog and for each library. + +Files and covers are served under `/opds/v2` too, so apps send the same credentials they used for the catalog. Publication metadata follows the Readium Web Publication model, so roles such as penciler and translator keep their own names. diff --git a/src/content/docs/self-hosting/reverse-proxy.md b/src/content/docs/self-hosting/reverse-proxy.md new file mode 100644 index 0000000..299d794 --- /dev/null +++ b/src/content/docs/self-hosting/reverse-proxy.md @@ -0,0 +1,71 @@ +--- +title: HTTPS and reverse proxies +description: Put Chek server behind Caddy, nginx or a Cloudflare Tunnel, and tell it which proxy to trust. +--- + +Chek server speaks plain HTTP. Before you open it beyond your home network, put it behind a reverse proxy that serves HTTPS. Away from your local network, the Chek app and the [web app](https://app.chek.page) only reach servers over HTTPS. + +## Trust your proxy + +Behind a proxy, every request seems to come from the proxy's address. Chek limits wrong passwords per address, so without help everyone would share one limit. Tell the server which addresses are your proxy: + +```sh +CHEK_TRUSTED_PROXIES=172.16.0.0/12 +``` + +On requests from those addresses, the client's address is read from `X-Forwarded-For`. Requests from anywhere else are taken at their own address, so clients can't pick an address by sending the header themselves. + +For a proxy in another container on the same Docker network, the Docker ranges `10.0.0.0/8,172.16.0.0/12,192.168.0.0/16` cover it. + +## Caddy + +Caddy gets and renews a certificate by itself and sends `X-Forwarded-For`. + +```caddy title="Caddyfile" +chek.example.org { + reverse_proxy chek:8080 +} +``` + +## nginx + +```nginx +server { + listen 443 ssl; + server_name chek.example.org; + # ssl_certificate and ssl_certificate_key as usual + + # Uploads can be large. Match CHEK_MAX_UPLOAD_BYTES if admins upload. + client_max_body_size 2g; + + location / { + proxy_pass http://127.0.0.1:8080; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + # Pages and files stream straight through. + proxy_buffering off; + } +} +``` + +With nginx on the host and Chek on `127.0.0.1`, set `CHEK_TRUSTED_PROXIES=127.0.0.1`. + +## Cloudflare + +Behind Cloudflare, whether through a Tunnel or a proxied DNS record, also set: + +```sh +CHEK_CLIENT_IP_HEADER=CF-Connecting-IP +``` + +A tunnel or proxy in between may replace `X-Forwarded-For`, so the server reads the client from Cloudflare's own header instead. It reads the header only from `CHEK_TRUSTED_PROXIES`, so set that to your `cloudflared` container or proxy too. This only helps if every request comes through Cloudflare: a proxy that can be reached directly lets clients send their own value. + +Cloudflare caps request bodies on its plans, 100 MB on Free and Pro, so larger uploads have to go around it. + +## Checklist + +- HTTPS on a name you control, such as `chek.example.org`. +- `CHEK_TRUSTED_PROXIES` set to the proxy, and `CHEK_CLIENT_IP_HEADER` behind Cloudflare. +- `CHEK_PUBLIC_URL=https://chek.example.org` if you use [sign-in providers](/self-hosting/sign-in-providers/). +- The container's port not published to the internet directly. Use `expose` instead of `ports` in Compose. diff --git a/src/content/docs/self-hosting/sign-in-providers.md b/src/content/docs/self-hosting/sign-in-providers.md new file mode 100644 index 0000000..31a8563 --- /dev/null +++ b/src/content/docs/self-hosting/sign-in-providers.md @@ -0,0 +1,60 @@ +--- +title: Sign-in providers +description: Let people sign in with OpenID Connect providers such as Authentik, Authelia, Keycloak, Pocket ID or Google. +--- + +Besides a password, people can sign in through any OpenID Connect provider. Each provider is set up with environment variables. + +## Set up + +1. Set `CHEK_PUBLIC_URL` to the address people reach the server at, such as `https://chek.example.org`. +2. In your provider, create a client and register this redirect URI. The server also logs it at startup. + + ``` + https://chek.example.org/api/v1/auth/callback + ``` + +3. List your providers' ids, and give each one its settings. In variable names the id is upper-cased, with `-` written as `_`. + +```sh +CHEK_PUBLIC_URL=https://chek.example.org +CHEK_OIDC_PROVIDERS=authentik +CHEK_OIDC_AUTHENTIK_ISSUER=https://auth.example.org/application/o/chek/ +CHEK_OIDC_AUTHENTIK_CLIENT_ID= +CHEK_OIDC_AUTHENTIK_CLIENT_SECRET= +CHEK_OIDC_AUTHENTIK_NAME=Authentik +CHEK_OIDC_AUTHENTIK_CREATE_USERS=true +CHEK_OIDC_AUTHENTIK_ADMIN_GROUPS=chek-admins +``` + +| Setting | Default | What it does | +| --- | --- | --- | +| `_ISSUER` | required | The provider's issuer URL, over HTTPS (plain HTTP only on loopback, for testing) | +| `_CLIENT_ID` | required | The client's id | +| `_CLIENT_SECRET` | none | The client's secret. Leave it out for a public client | +| `_NAME` | the id | The label on the sign-in button | +| `_SCOPES` | `openid profile email` | Scopes to ask for | +| `_CREATE_USERS` | `false` | Whether someone unknown gets a new account | +| `_ADMIN_GROUPS` | none | Comma-separated groups whose members become admins when their account is created | +| `_GROUPS_CLAIM` | `groups` | The claim that lists a person's groups | + +The server fetches each provider's discovery document the first time it's used, so a provider that's down never stops the server from starting. + +## Who gets in + +People are matched by the provider's subject id, never by email or name. + +- **With `_CREATE_USERS=true`**, someone the server doesn't know gets an account named from `preferred_username`, `name` or `email`, with a number added if the name is taken. They're an admin if they're in one of `_ADMIN_GROUPS` and a reader otherwise. Roles aren't changed on later sign-ins, and new readers start with no libraries until an admin grants some. +- **Otherwise**, people sign in with their password once and link the provider from their account. + +Accounts made by a provider have no password. [OPDS readers](/self-hosting/opds/) sign in with a password, so those people set one first. A provider can't be unlinked while it's someone's only way to sign in. + +## Apps + +Apps start a sign-in with a PKCE challenge and a redirect URI of their own, and get back a one-time code that only they can trade for a session. Redirect URIs must start with `CHEK_PUBLIC_URL` or a prefix in `CHEK_OIDC_REDIRECTS`, which is `chek://` by default for the Chek app. If you build your own app, add its scheme: + +```sh +CHEK_OIDC_REDIRECTS=chek://,myapp:// +``` + +Sign-ins in progress are kept in memory for 10 minutes, so restarting the server cancels them. diff --git a/src/content/docs/self-hosting/troubleshooting.md b/src/content/docs/self-hosting/troubleshooting.md new file mode 100644 index 0000000..aa28130 --- /dev/null +++ b/src/content/docs/self-hosting/troubleshooting.md @@ -0,0 +1,45 @@ +--- +title: Troubleshooting +description: Common problems running Chek server and what to do about them. +--- + +## The server won't start + +It names the variable it couldn't read and why, such as `CHEK_SETUP_TOKEN: use at least 32 characters`. Fix that variable. `docker logs chek` shows the message. + +## I can't find the setup token + +It's logged only while the server has no accounts. Look further back in `docker logs chek`, or set your own with `CHEK_SETUP_TOKEN` and restart. Once an admin exists, setup answers `409 already_set_up`. + +## A library is empty + +- Check the path is the one **inside the container**, such as `/library/comics`, not the one on your host. +- Check the container can read it. The image runs as uid 10001, so the files need to be readable by that user or by everyone. +- Wait for the scan to finish. `GET /api/v1/libraries` shows each library's `scan` state. +- Files whose format the server can't tell are skipped. Supported formats are EPUB, PDF, CBZ, CBR, CB7, CBT, MOBI, AZW3, FB2, plain text and Markdown. + +## A page or file answers `409 content_changed` + +The file changed on disk since it was scanned, so its stored page offsets no longer match. The server has already started a rescan; try again when it's done. + +## Everyone gets `429 too_many_attempts` + +The server sees every client at your proxy's address. Set [`CHEK_TRUSTED_PROXIES`](/self-hosting/reverse-proxy/#trust-your-proxy), and `CHEK_CLIENT_IP_HEADER` behind Cloudflare. + +## The app or web app can't connect + +- Away from your local network, the app needs an `https://` address. +- The web app at app.chek.page reaches only HTTPS servers, and only if its origin is in `CHEK_WEB_ORIGINS`. It is by default. +- Check `https:///health` answers from the device. + +## A Komga or Kavita library won't add + +Adding one checks the address and credentials first, and answers `400 source_check_failed` with Komga's or Kavita's reason. Check the URL is reachable from the Chek container, not just from your browser, and that the account has the [roles it needs](/self-hosting/komga-and-kavita/). + +## An extension's site keeps failing + +Some sites put a challenge in front of their pages. See [Sites behind a challenge](/self-hosting/extensions/#sites-behind-a-challenge). If verification passes but requests still fail, the site may be refusing your server's outbound address. + +## Still stuck + +Open an issue on [readchek/chek](https://github.com/readchek/chek/issues) with the server version, how you run it, and the log lines around the problem. Leave out tokens and passwords. diff --git a/src/content/docs/self-hosting/updating-and-backups.md b/src/content/docs/self-hosting/updating-and-backups.md new file mode 100644 index 0000000..edf3c79 --- /dev/null +++ b/src/content/docs/self-hosting/updating-and-backups.md @@ -0,0 +1,51 @@ +--- +title: Updating and backups +description: Move between releases and keep a copy of what can't be rebuilt. +--- + +## Updating + +Releases are tags on `main`, such as `0.3.0`, and each has an image of the same name. `latest` follows releases and `edge` follows `main`. + +```sh +docker pull ghcr.io/readchek/chek:latest +docker stop chek && docker rm chek +# Run the same `docker run` as before, with the same volumes. +``` + +With Compose, `docker compose pull && docker compose up -d`. With Dokploy, redeploy the application. + +Keep the data volume. The database migrates itself when a new version starts. When a release reads files differently, the migration asks for a rescan, so the first start after an upgrade may scan every library again. + +To stay on one release, use its tag, such as `ghcr.io/readchek/chek:0.3.0`, and change it when you're ready. Release notes are on [GitHub](https://github.com/readchek/chek/releases). + +## What's in the data folder + +| Path | What it is | If you lose it | +| --- | --- | --- | +| `chek.db` | Accounts, libraries, reading positions, bookmarks, highlights, metadata edits and settings, in SQLite | Gone for good. **Back this up** | +| `extensions/state.db` | Extensions' caches and cookie jars | Extensions start over; people may need to verify sites again | +| `extensions/*.wasm` | Installed extension components | Downloaded again on reinstall | +| `extensions/images/` | Cached covers and pages from extensions | Fetched again | +| `covers/` | Cover thumbnails | Made again on the next scan | + +Your books stay in your own folders, which Chek only reads. + +## Backing up + +The database runs in SQLite's WAL mode, so copying `chek.db` while the server runs can catch it halfway through a write. Either stop the server and copy the folder: + +```sh +docker stop chek +docker run --rm -v chek-data:/data -v "$PWD":/backup debian:trixie-slim \ + tar czf /backup/chek-data.tgz -C /data chek.db extensions/state.db +docker start chek +``` + +Or take a consistent copy while it runs with the `sqlite3` tool: + +```sh +sqlite3 /path/to/data/chek.db ".backup '/backups/chek.db'" +``` + +To restore, put `chek.db` back in an empty data folder before the server starts. diff --git a/src/content/docs/self-hosting/uploads.md b/src/content/docs/self-hosting/uploads.md new file mode 100644 index 0000000..09630c6 --- /dev/null +++ b/src/content/docs/self-hosting/uploads.md @@ -0,0 +1,21 @@ +--- +title: Uploads +description: Let admins add files to a folder library over HTTP. +--- + +Admins can upload a file into a folder library by path: + +```sh +curl -X PUT "$SERVER/api/v1/libraries/1/files/Saga/Saga%20001.cbz" \ + -H "Authorization: Bearer $TOKEN" \ + -H 'content-type: application/octet-stream' \ + --data-binary @"Saga 001.cbz" +``` + +- Files must end in `.epub`, `.pdf`, `.cbz`, `.cbr`, `.cb7` or `.cbt`. Missing folders on the path are created. +- The library's folder must be mounted read-write. Otherwise uploads answer `409 library_read_only`. +- `CHEK_MAX_UPLOAD_BYTES` caps each file, 2 GiB by default; larger files answer `413`. Raise your [proxy's body limit](/self-hosting/reverse-proxy/) to match. +- The file streams to a hidden temporary file beside its destination, so a scan never sees half a file. It must match its extension, and is then moved into place and added to the library. +- Uploads never replace a file. A path that's taken answers `409 file_exists`. +- Every part of the path must be a plain name, with no `.`, `..` or names starting with `.`. Folders that lead outside it through a link are refused. +- If a scan of the library is running, the upload waits up to 20 seconds for it, then answers `202` and finishes recording the file in the background. diff --git a/src/content/docs/self-hosting/users.md b/src/content/docs/self-hosting/users.md new file mode 100644 index 0000000..3c2e327 --- /dev/null +++ b/src/content/docs/self-hosting/users.md @@ -0,0 +1,42 @@ +--- +title: Users and access +description: Roles, library grants, sessions, sign-in limits and demo accounts. +--- + +## Roles and libraries + +- **Admins** add and change libraries and accounts, and see every library. +- **Readers** see only the libraries an admin granted them. Other libraries, and the series and publications in them, answer `404`, so a reader can't tell they exist. + +Create accounts with `POST /api/v1/users`, and change a role, password or grants with `PATCH /api/v1/users/{userId}`. Names are 1 to 64 characters and not case-sensitive at sign-in. Passwords are at least 8 characters. + +The server always keeps one admin. Demoting or deleting the last one answers `409 last_admin`. + +## Sessions + +- Signing in returns a random session token. The server stores only its hash. +- A session lasts 90 days after it was last used. +- People can list their own signed-in devices with `GET /api/v1/me/sessions` and sign any of them out. +- Changing a password signs out the account's other sessions. + +Passwords are hashed with Argon2id. + +## Wrong passwords + +After 10 wrong passwords from one address within 15 minutes, password checks from that address answer `429 too_many_attempts` until the oldest failure is 15 minutes old. The limit is per address, not per account, so nobody can lock someone else out by guessing their name. Signed-in sessions aren't affected. + +Behind a reverse proxy, set [`CHEK_TRUSTED_PROXIES`](/self-hosting/reverse-proxy/) or every client shares the proxy's limit. + +## Basic credentials + +Requests may send HTTP Basic credentials, a name and password, instead of a session token. [OPDS readers](/self-hosting/opds/) need this, since they can't hold a token. Accounts made by a [sign-in provider](/self-hosting/sign-in-providers/) have no password until their owner sets one. + +## Demo accounts + +`CHEK_DEMO_USERS` names accounts to treat as shared demos, whose password you publish. Create the accounts first, then list their names: + +```sh +CHEK_DEMO_USERS=demo +``` + +Demo accounts can't change their password, sessions, sign-in providers, extensions, repositories or annotations; those answer `403 demo_account`. Their reading positions answer as saved but aren't kept, and they see only their own session, so visitors never see each other's places or devices. They see Safe and Suggestive content only. diff --git a/src/styles/brand.css b/src/styles/brand.css new file mode 100644 index 0000000..aedf0db --- /dev/null +++ b/src/styles/brand.css @@ -0,0 +1,50 @@ +/* Chek's brand, shared with chek.page and the apps: electric blue on paper, + mist for quiet surfaces, Bricolage headlines and DM Sans text. Fonts are + served from the site, like chek.page's, so the page never calls another + origin. */ +@font-face { + font-family: Bricolage; + src: url("/fonts/bricolage-700-593306227576.woff2") format("woff2"); + font-weight: 700; + font-display: swap; +} +@font-face { + font-family: "DM Sans"; + src: url("/fonts/dm-sans-400-2ea1bf6e4127.woff2") format("woff2"); + font-weight: 400; + font-display: swap; +} +@font-face { + font-family: "DM Sans"; + src: url("/fonts/dm-sans-600-fd6f63245e24.woff2") format("woff2"); + font-weight: 600; + font-display: swap; +} + +:root { + --sl-font: "DM Sans", system-ui, sans-serif; + --sl-color-accent-low: #1a2147; + --sl-color-accent: #5b78ff; + --sl-color-accent-high: #c9d2ff; + --sl-color-black: #0e0f15; + --sl-color-gray-6: #171820; +} + +:root[data-theme="light"] { + --sl-color-accent-low: #dfe4fb; + --sl-color-accent: #2448e8; + --sl-color-accent-high: #1a34b8; + --sl-color-white: #171820; + --sl-color-black: #fafaf7; + --sl-color-gray-6: #eef0f8; + --sl-color-gray-7: #f4f5fa; +} + +h1, +h2, +h3, +.site-title, +.hero h1 { + font-family: Bricolage, "DM Sans", sans-serif; + font-weight: 700; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..969835d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "astro/tsconfigs/strict", + "compilerOptions": { + "types": ["bun"] + }, + "include": [".astro/types.d.ts", "**/*"], + "exclude": ["dist", ".cloudflare"] +} From 93b6545b7f8b849e22c2cdf9c1384e27b60d0fb2 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 25 Oct 2026 15:13:41 +0000 Subject: [PATCH 2/2] docs: rewrite every page to the technical-writing rules One mode per page, task titles for how-to guides, short sentences in the second person, conditions before instructions, and no dashes, semicolons or slashes. Provider settings move into the configuration reference, and AGENTS.md records the writing rules. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01PY1z7gbTSbKghKSF1tWxKN --- AGENTS.md | 16 ++- src/content/docs/api/overview.md | 74 ++++++---- src/content/docs/api/reading.md | 129 ++++++++++++------ src/content/docs/get-started/introduction.md | 30 ++-- src/content/docs/get-started/quick-start.md | 76 +++++++---- src/content/docs/index.mdx | 21 +-- .../docs/self-hosting/configuration.md | 83 +++++------ src/content/docs/self-hosting/extensions.md | 99 +++++++++----- src/content/docs/self-hosting/first-steps.md | 56 ++++---- src/content/docs/self-hosting/install.md | 89 ++++++------ .../docs/self-hosting/komga-and-kavita.md | 82 +++++------ src/content/docs/self-hosting/logs.md | 14 +- src/content/docs/self-hosting/opds.md | 32 +++-- .../docs/self-hosting/reverse-proxy.md | 76 ++++++----- .../docs/self-hosting/sign-in-providers.md | 69 +++++----- .../docs/self-hosting/troubleshooting.md | 48 ++++--- .../docs/self-hosting/updating-and-backups.md | 47 ++++--- src/content/docs/self-hosting/uploads.md | 34 +++-- src/content/docs/self-hosting/users.md | 51 ++++--- 19 files changed, 668 insertions(+), 458 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 273c4af..3e29a7f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -36,11 +36,17 @@ The build fetches `openapi.yaml` from GitHub, so it needs network. `bun run depl ## Writing -- Write for someone who has never seen Chek: what to do, in order, with commands that work when pasted. Explain why only where it changes what they do. -- Check every setting, endpoint and default against the code or `openapi.yaml` at `chekRelease` before writing it down. -- Short sentences, active voice, second person. Sentence case for titles. Be exact: real variable names, real error codes. -- Every page has a `description` in its front matter, used for search and link previews. -- Link between pages with root-relative paths ending in `/`, such as `/self-hosting/install/`. +Pages follow the pstack `technical-writing` and `unslop` skills. + +- Give each page one mode. The quick start is a tutorial. Task pages are how-to guides, titled by the task, such as "Serve Chek over HTTPS". Configuration and the API overview are reference. "What Chek is" is explanation. +- Talk to the reader as "you", in the present tense, with one instruction or one thought per sentence. Split sentences longer than about 25 words. +- Put the condition before the instruction: "To pin a release, set `CHEK_IMAGE`." +- No em dashes, semicolons, slashes, or idioms. Never "simply", "easy", or "quickly". +- Use the real name of every variable, endpoint, error code, and file. Call each thing by one name on every page. +- Check every setting, endpoint, and default against the code or `openapi.yaml` at `chekRelease` before you write it down. +- Indent code with tabs, except YAML. +- Every page has a `description` in its front matter. Search and link previews use it. +- Link between pages with root-relative paths that end in `/`, such as `/self-hosting/install/`. ## Pull requests diff --git a/src/content/docs/api/overview.md b/src/content/docs/api/overview.md index bae1810..7b16d01 100644 --- a/src/content/docs/api/overview.md +++ b/src/content/docs/api/overview.md @@ -1,73 +1,95 @@ --- title: API overview -description: How to sign in, page through lists and handle errors in Chek server's HTTP API. +description: Authentication, access, paging, errors, and caching in the Chek server API. --- -Chek server has a JSON API under `/api/v1`. The [Chek app](https://apps.apple.com/us/app/chek-books-comics/id6761271144) and web app use it, and you can build on it too: your own client, a script that adds libraries, a bot that tells you about new chapters. +Chek server has a JSON API under `/api/v1`. The Chek app and the web app use it, and your own clients can too. -The API is described by a hand-written OpenAPI 3.1 file, [`openapi.yaml`](https://github.com/readchek/chek/blob/main/openapi.yaml), which is the contract: the server changes in step with it, and each server release tags the version it implements. The [reference](/api/reference/) on this site is built from the file at the release these docs describe. To generate a client, use the file from the tag that matches your server. +[`openapi.yaml`](https://github.com/readchek/chek/blob/main/openapi.yaml) in the chek repository describes the API in OpenAPI 3.1. It's the contract: a change to the API changes that file in the same pull request. Each server release tags the version of the file that it implements. The [API reference](/api/reference/) on this site comes from the file at release 0.3.0. To generate a client, use the file at the tag that matches your server. ## Authentication -Every operation needs a session token, except `GET /health`, `POST /api/v1/setup` and signing in. +Every operation needs a session token except these three: + +- `GET /health` +- `POST /api/v1/setup` +- `POST /api/v1/auth/sign-in` + +Sign-in takes a name, a password, and an optional `device` label: ```sh curl -X POST $SERVER/api/v1/auth/sign-in \ - -H 'content-type: application/json' \ - -d '{"name": "sam", "password": "", "device": "My script"}' + -H 'content-type: application/json' \ + -d '{"name": "sam", "password": "", "device": "My script"}' ``` +The response holds the token and the account: + ```json -{ "token": "…", "user": { "id": 2, "name": "sam", "role": "reader", "libraryIds": [1] } } +{ + "token": "…", + "user": { "id": 2, "name": "sam", "role": "reader", "libraryIds": [1] } +} ``` -Send the token on every request: +Every other request sends the token in this header: ```http Authorization: Bearer ``` -- `device` labels the session in the person's list of signed-in devices. Name your client. -- Sessions last 90 days after their last use. `POST /api/v1/auth/sign-out` ends one. -- HTTP Basic credentials work too, for clients that can't keep a token, such as OPDS readers. Prefer tokens: they can be signed out one at a time, and a password change doesn't break other devices. -- For [sign-in providers](/self-hosting/sign-in-providers/), list them with `GET /api/v1/auth/providers`, then follow the PKCE flow in the reference under `authorize` and `exchange`. +| Fact | Value | +| --- | --- | +| Session length | 90 days after the last use | +| End a session | `POST /api/v1/auth/sign-out` | +| Session label | The `device` value. It appears in the person's list of signed-in devices | +| Basic credentials | Accepted on every operation that needs a token. OPDS readers use them | +| Sign-in providers | `GET /api/v1/auth/providers` lists them. The `authorize` and `exchange` operations in the reference carry the PKCE flow | + +A session token is safer to keep than a password. A person can end one session without changing the password, and a password change doesn't break other devices. ## Access -Admins see every library. Readers see only the libraries they were granted, and everything else answers `404`, as if it didn't exist. Admin-only operations answer `403` for readers. +| Caller | Sees | +| --- | --- | +| Admin | Every library | +| Reader | The libraries in the account's `libraryIds` | -## Browsers +A library that a reader can't see answers `404`, and so do its series and publications. An admin-only operation answers `403` to a reader. -Browsers may call the API only from the origins in [`CHEK_WEB_ORIGINS`](/self-hosting/configuration/#behind-a-proxy), `https://app.chek.page` by default. Requests carry a bearer token and never cookies. A page served over HTTPS can only call servers on HTTPS. +## Browsers -## Lists +The API answers browser requests only from the origins in `CHEK_WEB_ORIGINS`. The default is `https://app.chek.page`. Requests carry a bearer token and never cookies. A page served over HTTPS can call only HTTPS servers. -Lists answer `{ "items": [...] }`. Long lists are paged with an opaque cursor: +## Paging -```sh -curl "$SERVER/api/v1/libraries/1/publications?limit=100" -H "Authorization: Bearer $TOKEN" -``` +A list answers with an `items` array. A long list also has a `nextCursor`: ```json -{ "items": [ ... ], "nextCursor": "…" } +{ "items": [], "nextCursor": "…" } ``` -Pass `nextCursor` back as `cursor` for the next page. It's absent on the last page. `limit` is 50 by default and clamped to 1 through 200. Search pages by position instead, since relevance has no stable key. +| Parameter | Meaning | +| --- | --- | +| `cursor` | The `nextCursor` from the previous page. The last page has no `nextCursor` | +| `limit` | Items on each page. The default is 50. The server clamps values to the range 1 to 200 | + +Search pages by position instead of by cursor, because a relevance order has no stable key. ## Errors -Errors answer with an HTTP status and a body you can switch on: +An error has an HTTP status and a JSON body with a `code` and a `message`: ```json { "code": "progress_conflict", "message": "Another device saved a newer position." } ``` -Switch on `code`, not `message`. Messages explain the problem to a developer and may change. Each operation in the [reference](/api/reference/) lists the codes it can answer, such as `content_changed` when a file changed on disk since its last scan, or `too_many_attempts` after too many wrong passwords. +Clients branch on `code`. The `message` explains the problem to a developer and can change between releases. Each operation in the [API reference](/api/reference/) lists the codes that it returns. ## Caching -Files, pages and covers carry an `ETag`. Send it back as `If-None-Match` and an unchanged one answers `304`. Files support `Range` requests where the library can serve them. +Files, pages, and covers have an `ETag`. A request with a matching `If-None-Match` answers `304`. File downloads accept `Range` when the library's source supports it. ## OPDS -For reading apps rather than custom clients, the server also speaks [OPDS 2](/self-hosting/opds/) under `/opds/v2`. +For reading apps, the server also serves an OPDS 2 catalog under `/opds/v2`. Read [Browse Chek from an OPDS reader](/self-hosting/opds/). diff --git a/src/content/docs/api/reading.md b/src/content/docs/api/reading.md index 055bc8b..75a4fbf 100644 --- a/src/content/docs/api/reading.md +++ b/src/content/docs/api/reading.md @@ -1,88 +1,141 @@ --- -title: Read and sync -description: Walk a library, fetch pages and files, and keep a reading position in step across devices. +title: Read and sync from a client +description: Find publications, fetch pages and files, and keep reading positions and annotations in step across devices. --- -This walks through what a reading client does, using `curl`. Every request sends `Authorization: Bearer $TOKEN`; it's left out below for brevity. +This guide shows the requests that a reading client makes. Each request needs the `Authorization: Bearer ` header. The examples leave it out to stay short. ## Find something to read -```sh -GET /api/v1/libraries # the libraries you can open -GET /api/v1/libraries/{libraryId}/series # a library's series, in title order -GET /api/v1/series/{seriesId}/publications # a series' publications, in reading order -GET /api/v1/me/continue-reading # started and not finished, newest first -GET /api/v1/recently-added # newest first -GET /api/v1/search?q=saga # titles, series, contributors and publishers +These requests list what a person can read: + +```http +GET /api/v1/libraries +GET /api/v1/libraries/{libraryId}/series +GET /api/v1/series/{seriesId}/publications +GET /api/v1/me/continue-reading +GET /api/v1/recently-added +GET /api/v1/search?q=saga ``` -Each publication has a `format`. Comic archives (`cbz`, `cbr`, `cb7`, `cbt`) and extension chapters (`remote`) are read a page at a time. Everything else is read as a whole file. Pick how to open a publication from `format`, never from a URL or file name. +- A library's series come in title order. A series' publications come in reading order. +- Continue reading lists publications that the person started and didn't finish, newest first. +- Search matches titles, series names, contributors, and publishers. + +## Choose how to open a publication + +Each publication has a `format`. Choose the reader from `format`, never from a URL or a file name. + +- For comic archives, `cbz`, `cbr`, `cb7`, and `cbt`, and for extension chapters, `remote`, fetch one page at a time. +- For every other format, fetch the whole file. + +If `available` is `false`, the file is gone from disk or from its source. The server keeps its reading position in case it comes back. -`available: false` means the file has gone from disk or its source. It keeps its reading position in case it comes back. +## Fetch the pages of a comic -## Comics: pages +First, list the pages: -```sh +```http GET /api/v1/publications/{publicationId}/pages ``` ```json -{ "items": [{ "index": 0, "mediaType": "image/jpeg", "size": 412331 }, ...] } +{ "items": [{ "index": 0, "mediaType": "image/jpeg", "size": 412331 }] } ``` -Then fetch each page by its zero-based index. The bytes are the original image, unchanged. +Then fetch each page by its index, starting at `0`. The server sends the original image bytes. -```sh +```http GET /api/v1/publications/{publicationId}/pages/0 ``` -## Books: the file +## Fetch the file of a book -```sh +```http GET /api/v1/publications/{publicationId}/file ``` -The file is sent as it is on disk, with `Range` support, so a reader can open a large EPUB or PDF without downloading all of it first. Keep the `ETag` and send it as `If-None-Match` to check a downloaded copy is still current. +The server sends the file as it is on disk and accepts `Range` requests. A reader can open a large EPUB or PDF before the whole file arrives. Keep the `ETag`. To check that a downloaded copy is current, send the tag in `If-None-Match`. -Covers come from `GET /api/v1/publications/{publicationId}/cover` as JPEG thumbnails up to 400 by 600 pixels. `hasCover` says whether there is one. +To show a cover, fetch `GET /api/v1/publications/{publicationId}/cover` if `hasCover` is `true`. Covers are JPEG thumbnails that fit in 400 by 600 pixels. -## Positions +## Get the reading position -A reading position is a [Readium Locator](https://readium.org/architecture/models/locators/), stored as you send it, up to 16 KiB. [Chek Reader](https://github.com/readchek/chek-reader) and Readium's toolkits produce them as they are. +A reading position is a [Readium Locator](https://readium.org/architecture/models/locators/). The server stores it as the client sent it, up to 16 KiB. [Chek Reader](https://github.com/readchek/chek-reader) and Readium's toolkits make locators in this form. -```sh +```http GET /api/v1/publications/{publicationId}/progress ``` ```json -{ "revision": 4, "locator": { "href": "chapter3.xhtml", "type": "application/xhtml+xml", "locations": { "totalProgression": 0.31 } }, "device": "iPad", "updatedAt": "2026-10-25T09:12:00Z" } +{ + "revision": 4, + "locator": { + "href": "chapter3.xhtml", + "type": "application/xhtml+xml", + "locations": { "totalProgression": 0.31 } + }, + "device": "iPad", + "updatedAt": "2026-10-25T09:12:00Z" +} ``` -Revision `0` with a `null` locator means nothing is saved yet. +If nothing is saved, `revision` is `0` and `locator` is `null`. -To save, send the revision you last read: +## Save the reading position -```sh +Send the new locator with the revision that the client last read: + +```http PUT /api/v1/publications/{publicationId}/progress -{ "expectedRevision": 4, "locator": { ... } } ``` -If another device saved since, the answer is `409 progress_conflict` with that device's position in `current`: +```json +{ "expectedRevision": 4, "locator": { "href": "chapter3.xhtml", "type": "application/xhtml+xml" } } +``` + +If another device saved after that revision, the server answers `409 progress_conflict`. The `current` field holds the other device's position: ```json -{ "code": "progress_conflict", "message": "…", "current": { "revision": 5, "locator": { ... }, "device": "iPhone" } } +{ + "code": "progress_conflict", + "message": "…", + "current": { "revision": 5, "locator": {}, "device": "iPhone", "updatedAt": "…" } +} ``` -Ask the reader which position to keep, then save again with `expectedRevision` set to `current.revision`. The server never decides for them, because going back to reread something is as deliberate as reading on. +To resolve the conflict, follow these steps: + +1. Ask the reader which position to keep. +2. Save that position again with `expectedRevision` set to `current.revision`. -A `totalProgression` of 1 marks a publication finished, which takes it off Continue reading. +The server never picks a position itself. A reader who goes back to reread a chapter meant to go back. -## Bookmarks and highlights +When `totalProgression` is `1`, the publication is finished, and it leaves Continue reading. -```sh -GET /api/v1/publications/{publicationId}/annotations +## Sync bookmarks and highlights + +To list a publication's bookmarks and highlights, send this request: + +```http +GET /api/v1/publications/{publicationId}/annotations +``` + +To add a highlight, send its locator with the highlighted text in `text.highlight`: + +```http POST /api/v1/publications/{publicationId}/annotations -{ "kind": "highlight", "locator": { ..., "text": { "highlight": "the words" } }, "color": "yellow", "note": "…" } ``` -Change one with `PATCH /api/v1/annotations/{annotationId}` and remove it with `DELETE`. The last save wins. Fetch the whole list when opening a publication, so deletions made on another device show up. A publication holds up to 5,000 annotations per person. +```json +{ + "kind": "highlight", + "locator": { "href": "chapter3.xhtml", "type": "application/xhtml+xml", "text": { "highlight": "the words" } }, + "color": "yellow", + "note": "A note" +} +``` + +To change an annotation, send `PATCH /api/v1/annotations/{annotationId}`. To remove it, send `DELETE` to the same path. + +The last save wins. Fetch the whole list each time a publication opens, so that deletions from other devices appear. Each person can keep up to 5,000 annotations on one publication. diff --git a/src/content/docs/get-started/introduction.md b/src/content/docs/get-started/introduction.md index 40538d6..549f301 100644 --- a/src/content/docs/get-started/introduction.md +++ b/src/content/docs/get-started/introduction.md @@ -1,24 +1,26 @@ --- title: What Chek is -description: The app, the server, the extensions and the reading engine, and how they fit together. +description: About the Chek app, Chek server, extensions, and Chek Reader, and how they fit together. --- -Chek is a reader for books and comics wherever they're kept. It's made of a few pieces, and you only need the ones that fit what you're doing. +Chek is a reader for books and comics. It has five parts. You need only the parts that match what you want to do. -| Piece | What it is | Where it lives | +| Part | What it does | Repository | | --- | --- | --- | -| **Chek app** | The reader for iPhone and iPad, free on the [App Store](https://apps.apple.com/us/app/chek-books-comics/id6761271144). It reads your own files with no account, and connects to a Chek server. | [readchek/app](https://github.com/readchek/app) | -| **Chek web app** | Account and content settings at [app.chek.page](https://app.chek.page), talking to your server from the browser. | [readchek/app](https://github.com/readchek/app/tree/main/web) | -| **Chek server** | An open-source, self-hosted library server written in Rust. It scans folders, reads Komga and Kavita libraries, runs source extensions, syncs reading positions and speaks OPDS 2. | [readchek/chek](https://github.com/readchek/chek) | -| **Extensions** | Sources the server reads series from, built as WebAssembly components and installed from extension repositories. | [readchek/extensions](https://github.com/readchek/extensions) | -| **Chek Reader** | The Swift package that opens, lays out and pages publications in the app. It has no Chek-specific code, so any Apple app can use it. | [readchek/chek-reader](https://github.com/readchek/chek-reader) | +| Chek app | Reads books and comics on iPhone and iPad. It opens files on the device with no account, and it connects to Chek servers. It's free on the [App Store](https://apps.apple.com/us/app/chek-books-comics/id6761271144). | [readchek/app](https://github.com/readchek/app) | +| Chek web app | Changes account and content settings at [app.chek.page](https://app.chek.page). It calls your server from the browser. | [readchek/app](https://github.com/readchek/app/tree/main/web) | +| Chek server | Serves a library that you host yourself. It scans folders, reads Komga and Kavita servers, runs source extensions, syncs reading positions, and serves an OPDS 2 catalog. It's written in Rust. | [readchek/chek](https://github.com/readchek/chek) | +| Extensions | Add sources that the server reads series from. Each extension is a WebAssembly component that the server installs from an extension repository. | [readchek/extensions](https://github.com/readchek/extensions) | +| Chek Reader | Opens, lays out, and pages publications in the app. It's a Swift package with no Chek-specific code, so any Apple app can use it. | [readchek/chek-reader](https://github.com/readchek/chek-reader) | -The server is optional. The app reads files on the device without it. A server adds a library that follows you between devices, positions and highlights that sync, and sources from Komga, Kavita and extensions. +## The server is optional + +The app reads files on the device without a server. A server adds three things. Your library is the same on every device. Reading positions, bookmarks, and highlights sync between devices. And the app can read from Komga, Kavita, and extensions through the server. ## Pick your path -- **I want my own library server.** Start with the [quick start](/get-started/quick-start/), then [install it properly](/self-hosting/install/) behind HTTPS. -- **I want to build a client, script or integration.** Read the [API overview](/api/overview/) and the [API reference](/api/reference/). -- **I want my OPDS reader to browse Chek.** See [OPDS](/self-hosting/opds/). -- **I want to write a source.** The [extension guide](https://github.com/readchek/chek/blob/main/extensions/README.md) covers `chek ext` until its pages land here. -- **I want a reader in my own Swift app.** See [Chek Reader's README](https://github.com/readchek/chek-reader#readme) until its pages land here. +- To run your own library server, start with the [quick start](/get-started/quick-start/). Then [install the server](/self-hosting/install/) behind HTTPS. +- To build a client, a script, or an integration, read the [API overview](/api/overview/) and the [API reference](/api/reference/). +- To browse Chek from an OPDS reader, read [Browse Chek from an OPDS reader](/self-hosting/opds/). +- To write a source, read the [extension README](https://github.com/readchek/chek/blob/main/extensions/README.md). It covers the `chek ext` command. +- To add a reader to your own Swift app, read [the Chek Reader README](https://github.com/readchek/chek-reader#readme). diff --git a/src/content/docs/get-started/quick-start.md b/src/content/docs/get-started/quick-start.md index 51ea849..c89ebac 100644 --- a/src/content/docs/get-started/quick-start.md +++ b/src/content/docs/get-started/quick-start.md @@ -1,74 +1,94 @@ --- title: Quick start -description: Run Chek server with Docker, create the admin, add a folder and read it in the app. +description: Run Chek server in Docker on your computer, create the admin account, add a folder, and read it in the app. --- -This gets a server running on your own machine in a few minutes. It's good for trying Chek. Before opening it to other devices outside your home network, follow [Install](/self-hosting/install/) and [HTTPS and reverse proxies](/self-hosting/reverse-proxy/). +In this tutorial, we run Chek server on your computer, add a folder of comics to it, and read that folder in the Chek app. It takes about ten minutes. + +This setup is for trying Chek on your home network. Before other people use your server over the internet, follow [Install the server](/self-hosting/install/) and [Serve Chek over HTTPS](/self-hosting/reverse-proxy/). You need [Docker](https://docs.docker.com/get-docker/) and a folder of books or comics. -## 1. Start the server +## Start the server + +First, start the server. Replace `/path/to/comics` with your folder. ```sh docker run -d --name chek -p 8080:8080 \ - -v chek-data:/data \ - -v /path/to/comics:/library:ro \ - ghcr.io/readchek/chek:latest + -v chek-data:/data \ + -v /path/to/comics:/library:ro \ + ghcr.io/readchek/chek:latest ``` -- `chek-data` holds the database, covers and installed extensions. Keep it between upgrades. -- Your folder is mounted read-only at `/library`. The server never changes your files. +The `chek-data` volume holds the database and the covers. The second `-v` mounts your folder at `/library`. The `:ro` makes it read-only, so the server can't change your files. -Check it's up: +Now check that the server answers: ```sh curl localhost:8080/health ``` -## 2. Create the admin +You should see this: + +```json +{"status":"ok"} +``` + +If `curl` can't connect, run `docker logs chek` to see why the server stopped. + +## Create the admin account -A new server has no accounts. It prints a one-time setup token in its log so that only someone who can read the log can claim it: +A new server has no accounts. When it starts, it writes a one-time setup token to its log. Only a person who can read the log can create the first account. + +Find the token in the log: ```sh docker logs chek ``` -Trade the token for the first admin account: +Next, send the token with the name and password of the admin account. Replace the two values in angle brackets. ```sh curl -X POST localhost:8080/api/v1/setup \ - -H 'content-type: application/json' \ - -d '{"setupToken": "", "name": "admin", "password": ""}' + -H 'content-type: application/json' \ + -d '{"setupToken": "", "name": "admin", "password": ""}' ``` -The answer holds a session `token`. Keep it for the next step: +The response is a JSON object with a `token` field. Save the token in a shell variable, because the next steps use it: ```sh -TOKEN= +TOKEN= ``` -## 3. Add your folder +## Add your folder + +Now add the folder as a library. Use the path inside the container, `/library`, not the path on your computer. ```sh curl -X POST localhost:8080/api/v1/libraries \ - -H "Authorization: Bearer $TOKEN" \ - -H 'content-type: application/json' \ - -d '{"name": "Comics", "path": "/library"}' + -H "Authorization: Bearer $TOKEN" \ + -H 'content-type: application/json' \ + -d '{"name": "Comics", "path": "/library"}' ``` -The server starts scanning at once. Large folders take a while; the library's `scan` field shows how far it's got: +The server starts to scan the folder at once. A large folder takes some minutes. To see how far the scan is, list the libraries: ```sh curl localhost:8080/api/v1/libraries -H "Authorization: Bearer $TOKEN" ``` -## 4. Read +Each library in the response has a `scan` field with the state of its scan. + +## Read in the app + +Finally, open the Chek app on a device on the same network. Add a server with your computer's address and port, such as `192.168.1.20:8080`, and sign in as `admin`. Your comics appear in the app. + +The app uses plain HTTP only for addresses on your local network. For any other address, the server needs HTTPS. -- **In the Chek app**, add a server with your computer's address on your network, such as `192.168.1.20:8080`, and sign in as `admin`. The app uses plain HTTP only for addresses on your local network; anywhere else it needs HTTPS. -- **In an OPDS 2 reader** such as Thorium or KOReader, add `http://:8080/opds/v2/catalog` and sign in with the same name and password. +You can also read from an OPDS 2 reader, such as Thorium or KOReader. Add `http://:8080/opds/v2/catalog` as a catalog, and sign in with the same name and password. -## Next +## Next steps -- [Install](/self-hosting/install/) covers Compose, Dokploy and the image tags. -- [First steps](/self-hosting/first-steps/) adds readers and more libraries. -- [Configuration](/self-hosting/configuration/) lists every setting. +- [Install the server](/self-hosting/install/) with Compose or Dokploy, and choose an image tag. +- [Set up a new server](/self-hosting/first-steps/) with more libraries and accounts for other people. +- Look up a setting in the [configuration reference](/self-hosting/configuration/). diff --git a/src/content/docs/index.mdx b/src/content/docs/index.mdx index f01e719..f59781e 100644 --- a/src/content/docs/index.mdx +++ b/src/content/docs/index.mdx @@ -1,9 +1,9 @@ --- title: Chek Docs -description: Run your own Chek server, build on its API, write source extensions and embed Chek Reader. +description: Run your own Chek server, build on its API, write source extensions, and embed Chek Reader. template: splash hero: - tagline: Run your own Chek server, build on its API, write sources for it, and read with the same engine as the app. + tagline: Run your own Chek server, build a client on its API, and find out how the pieces of Chek fit together. actions: - text: Run a server link: /get-started/quick-start/ @@ -17,19 +17,20 @@ import { Card, CardGrid } from "@astrojs/starlight/components" - Run Chek server with Docker, point it at your folders, Komga or Kavita, and read from the app on - every device. [Install the server](/self-hosting/install/). + Run Chek server in Docker. Point it at your folders, a Komga server, or a Kavita server, and + read from the app on every device. Start with [Install](/self-hosting/install/). - Sign in, browse libraries, stream pages and sync reading positions over HTTP. - [API overview](/api/overview/). + Sign in, list libraries, fetch pages, and sync reading positions over HTTP. Start with the [API + overview](/api/overview/). - Sources are WebAssembly components the server runs in a sandbox. Guides are coming; for now, see - the [extension README](https://github.com/readchek/chek/blob/main/extensions/README.md). + A source extension is a WebAssembly component that the server runs in a sandbox. Until its + guides are on this site, read the [extension + README](https://github.com/readchek/chek/blob/main/extensions/README.md). - The Swift package behind the app reads EPUB, PDF, comics and more in any Apple app. Guides are - coming; for now, see [its README](https://github.com/readchek/chek-reader#readme). + Chek Reader is the Swift package that reads EPUB, PDF, and comics in the app. Until its guides are + on this site, read [the Chek Reader README](https://github.com/readchek/chek-reader#readme). diff --git a/src/content/docs/self-hosting/configuration.md b/src/content/docs/self-hosting/configuration.md index a343293..98d47e0 100644 --- a/src/content/docs/self-hosting/configuration.md +++ b/src/content/docs/self-hosting/configuration.md @@ -1,62 +1,69 @@ --- title: Configuration -description: Every environment variable Chek server reads. +description: Every environment variable that Chek server reads. --- -Chek server is configured only with environment variables, and all of them are optional. A value it can't read stops the server at startup with the variable's name and the reason, so a typo never runs with a default you didn't mean. +Chek server reads its settings from environment variables only. Every variable is optional. If the server can't read a value, it doesn't start, and the log names the variable and the reason. ## Basics -| Variable | Default | What it does | +| Variable | Default | Effect | | --- | --- | --- | -| `CHEK_BIND` | `127.0.0.1:8080`, or `0.0.0.0:8080` in the image | Address and port to listen on | -| `CHEK_DATA_DIR` | `./data`, or `/data` in the image | Where the database, covers and extensions live | -| `CHEK_SCAN_WORKERS` | cores − 1 | How many files a scan reads at once. Scanning on every core slows down pages for readers | -| `CHEK_SETUP_TOKEN` | logged at start | Your own [setup token](/self-hosting/first-steps/#create-the-admin), 32 characters or more, so you don't need the log | -| `CHEK_MAX_UPLOAD_BYTES` | `2147483648` (2 GiB) | Largest file an admin can [upload](/self-hosting/uploads/) | +| `CHEK_BIND` | `127.0.0.1:8080`. In the image, `0.0.0.0:8080` | The address and port to listen on | +| `CHEK_DATA_DIR` | `./data`. In the image, `/data` | The folder for the database, covers, and extensions | +| `CHEK_SCAN_WORKERS` | One less than the number of cores | The number of files that a scan reads at once | +| `CHEK_SETUP_TOKEN` | A random token, written to the log | The token that creates the first admin account. It must be 32 or more characters | +| `CHEK_MAX_UPLOAD_BYTES` | `2147483648`, which is 2 GiB | The largest file that an admin can upload | -## Behind a proxy +## Proxies and browsers -See [HTTPS and reverse proxies](/self-hosting/reverse-proxy/). - -| Variable | Default | What it does | +| Variable | Default | Effect | | --- | --- | --- | -| `CHEK_TRUSTED_PROXIES` | none | Your proxy's addresses or CIDR ranges, comma-separated, such as `10.0.0.0/8`. Client addresses are read from `X-Forwarded-For` only on requests from these | -| `CHEK_CLIENT_IP_HEADER` | none | A header to read the client address from instead, such as `CF-Connecting-IP` behind Cloudflare | -| `CHEK_WEB_ORIGINS` | `https://app.chek.page` | Comma-separated origins allowed to call the API from a browser. Add yours if you host a web client | +| `CHEK_TRUSTED_PROXIES` | None | Comma-separated addresses or CIDR ranges of your reverse proxies, such as `10.0.0.0/8`. The server reads the client address from `X-Forwarded-For` only on requests from these addresses | +| `CHEK_CLIENT_IP_HEADER` | None | A header to read the client address from, such as `CF-Connecting-IP`. The server reads it only on requests from `CHEK_TRUSTED_PROXIES` | +| `CHEK_WEB_ORIGINS` | `https://app.chek.page` | Comma-separated origins that may call the API from a browser | ## Sign-in -See [Sign-in providers](/self-hosting/sign-in-providers/). +| Variable | Default | Effect | +| --- | --- | --- | +| `CHEK_PUBLIC_URL` | None | The address that people use to reach the server. Sign-in providers need it | +| `CHEK_OIDC_PROVIDERS` | None | Comma-separated ids of OpenID Connect providers, such as `authentik,google` | +| `CHEK_OIDC_REDIRECTS` | `chek://` | Comma-separated prefixes, besides `CHEK_PUBLIC_URL`, that an app may give as its redirect URI | +| `CHEK_DEMO_USERS` | None | Comma-separated names of accounts that are shared demo accounts | + +### Provider settings + +Each provider in `CHEK_OIDC_PROVIDERS` takes the variables below. In each name, replace `` with the provider's id in upper case, with each `-` changed to `_`. -| Variable | Default | What it does | +| Variable | Default | Effect | | --- | --- | --- | -| `CHEK_PUBLIC_URL` | none | The address people reach the server at. Needed for sign-in providers | -| `CHEK_OIDC_PROVIDERS` | none | Comma-separated provider ids, such as `authentik,google` | -| `CHEK_OIDC__*` | | Each provider's settings | -| `CHEK_OIDC_REDIRECTS` | `chek://` | Comma-separated prefixes, besides the public URL, that apps may ask to be sent back to | -| `CHEK_DEMO_USERS` | none | Comma-separated account names to treat as shared [demo accounts](/self-hosting/users/#demo-accounts) | +| `CHEK_OIDC__ISSUER` | Required | The issuer URL. It must use HTTPS, except on a loopback address | +| `CHEK_OIDC__CLIENT_ID` | Required | The client id | +| `CHEK_OIDC__CLIENT_SECRET` | None | The client secret. A public client has none | +| `CHEK_OIDC__NAME` | The id | The label on the sign-in button | +| `CHEK_OIDC__SCOPES` | `openid profile email` | The scopes to request | +| `CHEK_OIDC__CREATE_USERS` | `false` | Whether a person with no account gets a new one | +| `CHEK_OIDC__ADMIN_GROUPS` | None | Comma-separated groups. A new account is an admin if the person is in one of them | +| `CHEK_OIDC__GROUPS_CLAIM` | `groups` | The claim that lists the person's groups | ## Sources and extensions -See [Komga and Kavita](/self-hosting/komga-and-kavita/) and [Extensions](/self-hosting/extensions/). - -| Variable | Default | What it does | +| Variable | Default | Effect | | --- | --- | --- | -| `CHEK_SOURCE_SYNC_MINUTES` | `60` | How often Komga, Kavita and extension libraries sync. `0` turns the timer off | -| `CHEK_EXTENSION_IMAGE_CACHE_MB` | `2048` | Disk cache for covers and pages from extensions. `0` turns it off | -| `CHEK_BROWSER_SOLVER_URL` | none | A FlareSolverr-compatible service, for sites behind a challenge | -| `CHEK_BROWSER_EXECUTABLE` | none, set in `-browser` images | Absolute path to Chromium, for extensions that drive interactive pages | -| `CHEK_BROWSER_NATIVE` | `false`, `true` in `-browser` images | Lets Chromium make its own connections through a restricted proxy | +| `CHEK_SOURCE_SYNC_MINUTES` | `60` | Minutes between syncs of Komga, Kavita, and extension libraries. `0` turns off the timer | +| `CHEK_EXTENSION_IMAGE_CACHE_MB` | `2048` | The size of the disk cache for images from extensions, in MB. `0` turns off the cache | +| `CHEK_BROWSER_SOLVER_URL` | None | The address of a FlareSolverr-compatible service | +| `CHEK_BROWSER_EXECUTABLE` | None. In `-browser` images, `/usr/bin/chromium` | The absolute path to Chromium | +| `CHEK_BROWSER_NATIVE` | `false`. In `-browser` images, `true` | Whether Chromium makes its own connections through a restricted proxy | | `CHEK_BROWSER_ARGS` | `["--headless=new"]` | Chromium launch arguments, as a JSON array | -| `CHEK_OUTBOUND_PROXY_URL` | none | An HTTP CONNECT proxy for all extension traffic | -| `CHEK_OUTBOUND_PROXY_USERNAME`, `CHEK_OUTBOUND_PROXY_PASSWORD` | none | The proxy's credentials, set together | +| `CHEK_OUTBOUND_PROXY_URL` | None | The address of an HTTP CONNECT proxy for all extension traffic | +| `CHEK_OUTBOUND_PROXY_USERNAME` | None | The proxy's user name. Set it with `CHEK_OUTBOUND_PROXY_PASSWORD` | +| `CHEK_OUTBOUND_PROXY_PASSWORD` | None | The proxy's password. Set it with `CHEK_OUTBOUND_PROXY_USERNAME` | ## Logs -See [Logs and tracing](/self-hosting/logs/). - -| Variable | What it does | -| --- | --- | -| `RUST_LOG` | Log level, such as `info` or `debug` | -| `OTEL_EXPORTER_OTLP_ENDPOINT` | Sends logs and traces to an OpenTelemetry collector over OTLP/HTTP. The standard `OTEL_*` variables apply | +| Variable | Default | Effect | +| --- | --- | --- | +| `RUST_LOG` | `info` | The log level, such as `debug` or `warn` | +| `OTEL_EXPORTER_OTLP_ENDPOINT` | None | The OpenTelemetry collector that receives logs and traces over OTLP/HTTP. The other standard `OTEL_*` variables apply too | diff --git a/src/content/docs/self-hosting/extensions.md b/src/content/docs/self-hosting/extensions.md index bef58d2..477b14b 100644 --- a/src/content/docs/self-hosting/extensions.md +++ b/src/content/docs/self-hosting/extensions.md @@ -1,59 +1,84 @@ --- -title: Extensions on your server -description: Repositories, installs, extension libraries, content ratings and sites behind a challenge. +title: Run extensions +description: Add extension repositories, turn extensions into libraries, and set up challenge solving and an outbound proxy. --- -Extensions are sources the server reads series from, such as a webcomic's site or a public-domain library. Each one is a WebAssembly component that runs in a sandbox inside the server, with no files and no network except the sites it declares. This page covers running them. Writing one is covered in the [extension guide](https://github.com/readchek/chek/blob/main/extensions/README.md) until its pages land here. +An extension is a source that the server reads series from, such as a webcomic's site or a library of public-domain books. Each extension is a WebAssembly component. The server runs it in a sandbox with no file access and no network access except to the sites that the extension declares. -## Repositories +This page is about running extensions on your server. To write one, read the [extension README](https://github.com/readchek/chek/blob/main/extensions/README.md). -Extensions are installed from repositories: an `index.json` and the components it lists, served over HTTPS. +Most people manage repositories and installs in the Chek app, so they never call the endpoints on this page. -- **Server-wide repositories** are the admin's allow list, and everyone sees them. A new server starts with the official one, `https://chek.page/extensions/index.json`, which an admin can remove. -- **Personal repositories** are added by people other than admins, and only they see them. This is off until an admin turns on `allowAnyRepository`: +## Add an extension repository - ```sh - curl -X PUT $SERVER/api/v1/settings \ - -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ - -d '{"allowAnyRepository": true}' - ``` +A repository is an `index.json` file and the components that it lists, served over HTTPS. There are two kinds: - Turning it off again keeps those repositories but nothing is installed or run from them. +- A server-wide repository belongs to the admins, and every user sees it. A new server starts with the official repository, `https://chek.page/extensions/index.json`. An admin can remove it. +- A personal repository belongs to one user, and only that user sees it. -The server reads every index again every six hours, so new versions show up as updates. Private repositories take a bearer token, a username and password, or a header, which are sent only to the repository's own origin and never returned by the API. +By default, only admins can add repositories. To let every user add personal repositories, turn on `allowAnyRepository`: -The Chek app manages repositories and installs, so most people never call these endpoints themselves. +```sh +curl -X PUT $SERVER/api/v1/settings \ + -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ + -d '{"allowAnyRepository": true}' +``` + +If you turn the setting off again, the server keeps the personal repositories. It doesn't install or run anything from them. + +The server reads every repository's index every six hours, so new versions appear as updates. A private repository takes a bearer token, a user name and password, or a header. The server sends them only to the repository's own origin and never returns them from the API. -## Installs +## Install an extension -Everyone installs extensions for themselves. Installing downloads the component, checks its SHA-256 digest, and checks that it declares the same id, version, sites and capabilities as the index, so a repository can't list one thing and ship another. Components are kept in the data folder under `extensions/`, shared by everyone on the same version. +Each user installs extensions for themselves. When a user installs one, the server does these checks: -## Extension libraries +1. It downloads the component and checks its SHA-256 digest against the index. +2. It checks that the component declares the same id, version, sites, and capabilities as the index. -An admin can turn an extension they installed into a library that readers are granted like any other. It starts empty and holds only the series an admin follows into it, synced on the same schedule as [Komga and Kavita](/self-hosting/komga-and-kavita/#syncing). It runs on that admin's install, so readers don't need the extension. If the admin uninstalls it, the library answers `source_unavailable` until it's installed again. +Because of the second check, a repository can't list one extension and ship another. The server stores components in the data folder under `extensions/`. Users on the same version share one copy. -## Content ratings +## Turn an extension into a library -Each person chooses which content ratings they see, from Safe and Suggestive through Erotica and Pornographic. Search passes the choice to the extension and drops results rated outside it, so a source that ignores the choice still can't show them. Until someone chooses, and always on demo accounts, they see Safe and Suggestive. +An admin can make a library from an extension that the admin installed. An admin grants the library to readers like any other library. -The iOS app only narrows the choice. Erotica and Pornographic are turned on in the web app at [app.chek.page](https://app.chek.page), after the person confirms they're an adult. +- The library starts empty. It holds only the series that an admin follows into it. +- It syncs on the same schedule as [Komga and Kavita libraries](/self-hosting/komga-and-kavita/#sync-a-library). +- It runs on the admin's install, so readers don't need the extension. +- If the admin uninstalls the extension, the library answers `source_unavailable` until the admin installs it again. -## Images +## Choose content ratings -Covers and pages from extensions are fetched by the server, never by the app, and kept on disk in `extensions/images` in the data folder. `CHEK_EXTENSION_IMAGE_CACHE_MB` caps it, 2048 by default; the least recently read images go first. `0` turns the cache off. +Each person chooses the content ratings they see. The ratings are Safe, Suggestive, Erotica, and Pornographic. The server passes the choice to the extension's search. Then it removes any result that the extension rated outside the choice, so a source that ignores the choice can't show those results. -## Sites behind a challenge +Until a person chooses, the person sees Safe and Suggestive. Demo accounts always see Safe and Suggestive. -Some sites put a browser challenge in front of their pages. Extensions that declare the `browser` capability can get past one in two ways, and both are the admin's to set up. +In the iOS app, a person can only narrow the choice. To turn on Erotica and Pornographic, the person confirms their age in the web app at [app.chek.page](https://app.chek.page). -- **A solver.** Point `CHEK_BROWSER_SOLVER_URL` at a [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr)-compatible service, such as `http://flaresolverr:8191/v1`. When a request meets a challenge, the server has the solver load the page, keeps the cookies it earned and retries once. -- **A browser in the server.** The `-browser` images include Chromium, for extensions that drive interactive pages. Give the container `shm_size: 1gb`. Chromium's own sandbox is turned off for container compatibility, so run only extensions you trust with these images. +## Size the image cache -A signed-in person can open `/verify` on the server to complete a site's checkbox challenge themselves for an installed source. The cookies are kept in the data folder until the site expires them. +The server fetches covers and pages from extensions. The app never contacts an extension's sites. The server keeps the images on disk in `extensions/images` in the data folder. -## Outbound proxy +`CHEK_EXTENSION_IMAGE_CACHE_MB` sets the size of that folder. The default is 2048. When the folder is full, the server deletes the images that were read least recently. A value of `0` turns off the cache. -To send all extension traffic through one HTTP CONNECT proxy: +## Let extensions get past site challenges + +Some sites show a browser challenge before their pages. An extension that declares the `browser` capability can get past a challenge in two ways. The admin sets up both. + +To use a solver, set `CHEK_BROWSER_SOLVER_URL` to a [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr)-compatible service: + +```sh +CHEK_BROWSER_SOLVER_URL=http://flaresolverr:8191/v1 +``` + +When a request meets a challenge, the server asks the solver to load the page. It keeps the cookies that the solver earned and tries the request once more. + +To run a browser in the server, use a `-browser` image and give the container 1 GB of shared memory with `shm_size: 1gb`. These images include Chromium, for extensions that use interactive pages. Chromium's own sandbox is off in these images, so run only extensions that you trust. + +A signed-in user can also complete a site's checkbox challenge by hand. The user opens `/verify` on the server and picks an installed source. The server keeps the cookies in the data folder until the site expires them. + +## Send extension traffic through a proxy + +To send all extension traffic through one HTTP CONNECT proxy, set these variables: ```sh CHEK_OUTBOUND_PROXY_URL=http://proxy.internal:8888 @@ -61,8 +86,10 @@ CHEK_OUTBOUND_PROXY_USERNAME= CHEK_OUTBOUND_PROXY_PASSWORD= ``` -- Only HTTP CONNECT proxies work, not SOCKS or HTTPS proxy endpoints. Keep credentials in their own variables, not in the URL. -- It covers source requests, images and browser traffic. Repository downloads, sign-in providers and telemetry connect directly. -- If the proxy fails, requests fail. They never fall back to a direct connection. -- Run the proxy where it can't reach your private network, since it resolves the names sources ask for. -- Without it, sources connect directly and ignore `HTTP_PROXY` and `HTTPS_PROXY`. +- The proxy must be an HTTP CONNECT proxy. SOCKS proxies and HTTPS proxy endpoints don't work. +- Put the credentials in their own variables, not in the URL. +- The proxy carries source requests, images, and browser traffic. Repository downloads, sign-in providers, and telemetry connect directly. +- If the proxy fails, the requests fail. The server never falls back to a direct connection. +- The proxy resolves the host names that sources ask for. Run it where it can't reach your private network. + +Without these variables, sources connect directly. They ignore `HTTP_PROXY` and `HTTPS_PROXY`. diff --git a/src/content/docs/self-hosting/first-steps.md b/src/content/docs/self-hosting/first-steps.md index 300c05f..f6dfbb4 100644 --- a/src/content/docs/self-hosting/first-steps.md +++ b/src/content/docs/self-hosting/first-steps.md @@ -1,62 +1,68 @@ --- -title: First steps -description: Claim a new server, add libraries and give people their own accounts. +title: Set up a new server +description: Create the admin account, add libraries, and create accounts for other people. --- -Libraries and accounts are managed through the API for now, so these steps use `curl`. Every request after setup sends the admin's session token as `Authorization: Bearer `. +The app doesn't manage libraries or accounts yet, so you do these steps with `curl`. After setup, every request sends the admin's session token in an `Authorization: Bearer ` header. + +Set the server's address in a shell variable first: ```sh -SERVER=https://chek.example.org # or http://localhost:8080 +SERVER=https://chek.example.org ``` -## Create the admin +## Create the admin account -A server with no accounts accepts one setup request. The token is `CHEK_SETUP_TOKEN` if you set it, or the one-time token in the log otherwise (`docker logs chek`). +A server with no accounts accepts one setup request. If you set `CHEK_SETUP_TOKEN`, use that value as the token. If you didn't, find the one-time token in the log with `docker logs chek`. ```sh curl -X POST $SERVER/api/v1/setup \ - -H 'content-type: application/json' \ - -d '{"setupToken": "", "name": "admin", "password": ""}' + -H 'content-type: application/json' \ + -d '{"setupToken": "", "name": "admin", "password": ""}' ``` -The answer is a session. Save its `token`: +The response is a session. Save its `token`: ```sh -TOKEN= +TOKEN= ``` -Once an admin exists, setup answers `409 already_set_up`. To sign in again later, use `POST /api/v1/auth/sign-in` with the same name and password. +After the first admin exists, setup answers `409 already_set_up`. To get a new token later, sign in with `POST /api/v1/auth/sign-in` and the same name and password. -## Add libraries +## Add a folder library -A library is a folder on the server, a [Komga or Kavita server](/self-hosting/komga-and-kavita/), or a source run by an [extension](/self-hosting/extensions/). For a folder, give the path inside the container: +To add a folder, give its path inside the container: ```sh curl -X POST $SERVER/api/v1/libraries \ - -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ - -d '{"name": "Books", "path": "/library/books"}' + -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ + -d '{"name": "Books", "path": "/library/books"}' ``` -The first scan starts at once. Every folder library is scanned again when the server starts, skipping files whose size and modification time haven't changed. To pick up changes while it runs, ask for a scan: +The server starts the first scan at once. It also scans every folder library each time it starts. That scan skips files whose size and modification time haven't changed. + +To pick up changes while the server runs, start a scan: ```sh curl -X POST $SERVER/api/v1/libraries/1/scan -H "Authorization: Bearer $TOKEN" ``` -## Add readers +A library can also read a [Komga or Kavita server](/self-hosting/komga-and-kavita/) or run an [extension](/self-hosting/extensions/#turn-an-extension-into-a-library). + +## Create accounts for other people -There are two roles. **Admins** manage libraries and accounts and see every library. **Readers** see only the libraries they were granted; anything else answers as not found. +An account is an admin or a reader. A reader sees only the libraries in its `libraryIds`. ```sh curl -X POST $SERVER/api/v1/users \ - -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ - -d '{"name": "sam", "password": "", "role": "reader", "libraryIds": [1, 2]}' + -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ + -d '{"name": "sam", "password": "<8 or more characters>", "role": "reader", "libraryIds": [1, 2]}' ``` -Change someone's role, password or libraries with `PATCH /api/v1/users/{userId}`. See [Users and access](/self-hosting/users/) for sessions, sign-in limits and demo accounts. +To change an account's role, password, or libraries, send `PATCH /api/v1/users/{userId}`. For the rules on roles, sessions, and sign-in limits, read [Accounts and access](/self-hosting/users/). -## Connect +## Connect a client -- **Chek app:** add the server's address and sign in. The app reads your libraries, syncs your place, and manages extension repositories and installs. -- **OPDS readers:** see [OPDS](/self-hosting/opds/). -- **Your own code:** see the [API overview](/api/overview/). +- In the Chek app, add the server's address and sign in. The app reads your libraries, syncs your place, and manages extension repositories and installs. +- In an OPDS reader, follow [Browse Chek from an OPDS reader](/self-hosting/opds/). +- In your own code, start with the [API overview](/api/overview/). diff --git a/src/content/docs/self-hosting/install.md b/src/content/docs/self-hosting/install.md index 400bb34..64b7a0f 100644 --- a/src/content/docs/self-hosting/install.md +++ b/src/content/docs/self-hosting/install.md @@ -1,39 +1,43 @@ --- -title: Install -description: Run Chek server with Docker, Docker Compose or Dokploy, or build it from source. +title: Install the server +description: Run Chek server with Docker, Docker Compose, or Dokploy, or build it from source. --- -Chek server runs as a single process with an SQLite database, so one container and one volume is a whole install. +Chek server is one process with one SQLite database. To install it, you run one container with one data volume. -## Images +## Choose an image tag -CI publishes `ghcr.io/readchek/chek` for amd64 and arm64. +CI publishes the image `ghcr.io/readchek/chek` for amd64 and arm64. -| Tag | What you get | +| Tag | Contents | | --- | --- | | `latest` | The newest release | -| A version, such as `0.3.0` | That release, for pinning | +| A version, such as `0.3.0` | That release | | `edge` | The newest commit on `main` | -| Any of the above with `-browser`, such as `latest-browser` | The same server with Chromium, for [browser-backed extensions](/self-hosting/extensions/#sites-behind-a-challenge) | +| Any tag above with `-browser` added, such as `latest-browser` | The same server with Chromium, for extensions that need a browser | -The plain image is small and has no browser. Use a `-browser` tag only if you run extensions that need one, and only extensions you trust. +Use the plain image unless an extension you run needs a browser. The `-browser` images turn off Chromium's own sandbox, so use them only with extensions you trust. For details, read [Let extensions get past site challenges](/self-hosting/extensions/#let-extensions-get-past-site-challenges). -In the image, the server listens on `0.0.0.0:8080`, keeps its data in `/data` and runs as an unprivileged `chek` user (uid 10001). +In every image, the server listens on `0.0.0.0:8080` and keeps its data in `/data`. It runs as the user `chek`, with uid 10001. -## Docker +## Run it with Docker + +Mount each folder that you want to read under `/library`: ```sh docker run -d --name chek --restart unless-stopped -p 8080:8080 \ - -e CHEK_SETUP_TOKEN=<32 or more random characters> \ - -v chek-data:/data \ - -v /srv/books:/library/books:ro \ - -v /srv/comics:/library/comics:ro \ - ghcr.io/readchek/chek:latest + -e CHEK_SETUP_TOKEN=<32 or more random characters> \ + -v chek-data:/data \ + -v /srv/books:/library/books:ro \ + -v /srv/comics:/library/comics:ro \ + ghcr.io/readchek/chek:latest ``` -Mount each folder you want to read under `/library`, read-only unless you want admins to [upload](/self-hosting/uploads/) into it. Setting `CHEK_SETUP_TOKEN` saves reading it from the log on first start. +`CHEK_SETUP_TOKEN` sets the token that creates the first admin account, so you don't need to read it from the log. Mount a folder read-write, without `:ro`, only if admins [upload files](/self-hosting/uploads/) to it. + +## Run it with Docker Compose -## Docker Compose +Create `compose.yaml`: ```yaml title="compose.yaml" services: @@ -52,43 +56,52 @@ volumes: chek-data: ``` +Then start it with a random setup token: + ```sh CHEK_SETUP_TOKEN=$(openssl rand -hex 24) docker compose up -d ``` -Behind a reverse proxy on the same Docker network, replace `ports` with `expose: ["8080"]` and set [`CHEK_TRUSTED_PROXIES`](/self-hosting/reverse-proxy/). +If a reverse proxy runs on the same Docker network, replace `ports` with `expose: ["8080"]`. Then set `CHEK_TRUSTED_PROXIES`, as [Serve Chek over HTTPS](/self-hosting/reverse-proxy/) describes. -For browser-backed extensions, use a `-browser` tag and give the container more shared memory for Chromium: +If an extension needs a browser, use a `-browser` tag and give Chromium 1 GB of shared memory: ```yaml image: ghcr.io/readchek/chek:latest-browser shm_size: 1gb ``` -## Dokploy +## Run it on Dokploy -The chek repository has a Compose file ready for Dokploy at [`deploy/server/compose.yaml`](https://github.com/readchek/chek/blob/main/deploy/server/compose.yaml). It runs the `edge-browser` image behind Dokploy's proxy. +The chek repository has a Compose file for Dokploy at [`deploy/server/compose.yaml`](https://github.com/readchek/chek/blob/main/deploy/server/compose.yaml). It runs the `edge-browser` image. -1. Create a **Compose** application from `readchek/chek` and select `deploy/server/compose.yaml`. -2. In **Environment**, set `CHEK_SETUP_TOKEN` to 32 or more random characters. To pin a release instead of following `main`, set `CHEK_IMAGE`, such as `ghcr.io/readchek/chek:0.3.0-browser`. -3. Route your domain to service `chek`, port `8080`, with HTTPS. -4. Deploy, then [create the admin](/self-hosting/first-steps/). +1. Create a Compose application from `readchek/chek`, and select `deploy/server/compose.yaml`. +2. In **Environment**, set `CHEK_SETUP_TOKEN` to 32 or more random characters. +3. To run a release instead of `main`, also set `CHEK_IMAGE`, such as `ghcr.io/readchek/chek:0.3.0-browser`. +4. Route your domain to the service `chek` on port `8080`, with HTTPS. +5. Deploy, then [create the admin account](/self-hosting/first-steps/#create-the-admin-account). -That file mounts no book folders. To read local files, add a read-only bind mount such as `/srv/books:/library/books:ro`. +The Compose file mounts no folders of books. To read files from the host, add a read-only bind mount, such as `/srv/books:/library/books:ro`. -## From source +## Build it from source -You need a recent stable Rust, [libvips](https://www.libvips.org) for cover thumbnails and [libarchive](https://www.libarchive.org) for RAR and 7-Zip comics. +To build the server, you need a recent stable Rust and two libraries. Chek uses [libvips](https://www.libvips.org) to make cover thumbnails and [libarchive](https://www.libarchive.org) to read RAR and 7-Zip comics. -```sh -apt install libvips-dev libarchive-dev # Debian or Ubuntu -brew install vips libarchive # macOS +1. Install the libraries. -git clone https://github.com/readchek/chek -cd chek -cargo run --release --bin chek -``` + ```sh + apt install libvips-dev libarchive-dev # Debian or Ubuntu + brew install vips libarchive # macOS + ``` + +2. Clone the repository and run the server. + + ```sh + git clone https://github.com/readchek/chek + cd chek + cargo run --release --bin chek + ``` -Run from source, the server listens on `127.0.0.1:8080` and keeps its data in `./data`. Change both with [`CHEK_BIND` and `CHEK_DATA_DIR`](/self-hosting/configuration/). +A server built from source listens on `127.0.0.1:8080` and keeps its data in `./data`. To change them, set `CHEK_BIND` and `CHEK_DATA_DIR`. -To build the image yourself, run `docker build -t chek .` in the repository, or `docker build --target browser -t chek-browser .` for the browser image. +To build the image yourself, run `docker build -t chek .` in the repository. For the image with Chromium, run `docker build --target browser -t chek-browser .`. diff --git a/src/content/docs/self-hosting/komga-and-kavita.md b/src/content/docs/self-hosting/komga-and-kavita.md index eb86ef2..c98dc8c 100644 --- a/src/content/docs/self-hosting/komga-and-kavita.md +++ b/src/content/docs/self-hosting/komga-and-kavita.md @@ -1,60 +1,66 @@ --- -title: Komga and Kavita -description: Read a Komga or Kavita server's libraries through Chek, with progress written back. +title: Read Komga and Kavita libraries +description: Add a Komga or Kavita server as a Chek library, and write reading progress back to it. --- -An admin can add a [Komga](https://komga.org) or [Kavita](https://www.kavitareader.com) server as a library. Everyone granted that library in Chek reads what one Komga or Kavita account sees, with pages and files fetched through Chek, so Komga or Kavita keeps the only copy. +An admin can add a [Komga](https://komga.org) or [Kavita](https://www.kavitareader.com) server as a library. Everyone who has the library reads what one Komga or Kavita account can see. Chek fetches pages and files from that server on each request, so the files stay only on the Komga or Kavita server. -Use an account made for Chek. Its credentials are checked before the library is added, stored on the server, and never returned by the API. +Create an account in Komga or Kavita for Chek to use. Chek checks the credentials before it adds the library. It stores them on the server and never returns them from the API. -## Komga +## Add a Komga library -The account needs Komga's **page streaming** and **file download** roles. Use an API key made in Komga for that account, or its email and password. +The Komga account needs the page streaming role and the file download role. -```sh -curl -X POST $SERVER/api/v1/libraries \ - -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ - -d '{"name": "Komga", "komga": {"url": "http://komga:25600", "apiKey": ""}}' -``` +1. Create an API key for the account in Komga. +2. Add the library: -| Field | What it does | -| --- | --- | -| `url` | Komga's address, including any path it's served under | -| `apiKey`, or `username` and `password` | The account to read with | -| `libraryIds` | Komga library ids to read. Leave it out for every library the account sees | -| `writeProgress` | `true` by default. Writes your reading position back to Komga | + ```sh + curl -X POST $SERVER/api/v1/libraries \ + -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ + -d '{"name": "Komga", "komga": {"url": "http://komga:25600", "apiKey": ""}}' + ``` -A sync reads the series and the books Komga has finished analyzing, with their titles, numbers, summaries, dates, authors, publisher, language and reading direction. Komga always sends whole files, even when asked for part of one. +The `url` is the address of Komga as the Chek container sees it, including any path that Komga is served under. -## Kavita +- To sign in with the account's email and password instead of a key, send `username` and `password`. +- To read only some Komga libraries, send their ids in `libraryIds`. -The account needs Kavita's **download** role for files. Copy its API key from the account's settings page in Kavita. +Chek reads Komga's series and the books that Komga has finished analyzing. Komga always sends whole files, even when a reader asks for a byte range. -```sh -curl -X POST $SERVER/api/v1/libraries \ - -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ - -d '{"name": "Kavita", "kavita": {"url": "http://kavita:5000", "apiKey": ""}}' -``` +## Add a Kavita library -The fields are the same as Komga's, except that Kavita takes only an API key and its `libraryIds` are numbers. +The Kavita account needs the download role. -Each Kavita chapter becomes one publication. A file that's a whole volume is called "Volume 1", a loose chapter "Chapter 12", and a special by its name, unless the file's own metadata gives a title. +1. Copy the account's API key from its settings page in Kavita. +2. Add the library: -## Syncing + ```sh + curl -X POST $SERVER/api/v1/libraries \ + -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ + -d '{"name": "Kavita", "kavita": {"url": "http://kavita:5000", "apiKey": ""}}' + ``` -Komga and Kavita can't tell Chek when something changes, so Chek syncs them: +To read only some Kavita libraries, send their numeric ids in `libraryIds`. -- when the server starts, -- every `CHEK_SOURCE_SYNC_MINUTES`, 60 by default, or never with `0`, -- and when an admin asks, with `POST /api/v1/libraries/{libraryId}/scan`. +Chek makes one publication for each Kavita chapter. If the file's own metadata has no title, Chek names it by its kind. A file that holds a whole volume is "Volume 1", a single chapter is "Chapter 12", and a special keeps its name. -A book is read again only when it, its file or its series changed. Books that disappear are marked unavailable, not deleted, so reading positions survive if they come back. Progress, bookmarks, highlights and an admin's metadata edits are kept in Chek and survive syncs. +## Sync a library -## Progress written back +Komga and Kavita can't tell Chek about changes, so Chek syncs on its own. A sync runs in three cases: -The admin who adds the library has their progress written back to the Komga or Kavita account, so its own apps resume where Chek left off. Only that one person is written back, because everyone reading the library shares the one account. Send `"writeProgress": false` to turn it off. +- When the server starts. +- Every `CHEK_SOURCE_SYNC_MINUTES` minutes. The default is 60. A value of `0` turns off the timer. +- When an admin sends `POST /api/v1/libraries/{libraryId}/scan`. -- **Komga:** EPUB positions go as Readium locators, and comics and PDFs as a page number. -- **Kavita:** comics and PDFs go as a page number. Kavita keeps a book's place in a form a Readium locator can't name, so a book is written back only once it's finished. +A sync reads a book again only if the book, its file, or its series changed. A book that has gone from Komga or Kavita becomes unavailable, but Chek doesn't delete it. If the book comes back, its reading position is still there. Chek keeps progress, bookmarks, highlights, and metadata edits, so a sync doesn't change them. -Reaching the last page marks a book read. A write that Komga or Kavita refuses is logged, not retried. +## Write progress back + +Chek writes the reading progress of the admin who added the library back to the Komga or Kavita account. Then Komga's or Kavita's own apps resume where Chek stopped. Chek writes back only that admin's progress, because every reader of the library shares the one account. + +To turn this off, send `"writeProgress": false` when you add the library. + +- For Komga, Chek sends EPUB positions as Readium locators. It sends comics and PDFs as a page number. +- For Kavita, Chek sends comics and PDFs as a page number. Kavita stores a book's position in a form that a Readium locator can't express. So Chek writes a book back only when the book is finished. + +When the reader reaches the last page, Chek marks the book as read. If Komga or Kavita refuses a write, Chek logs it and doesn't try again. diff --git a/src/content/docs/self-hosting/logs.md b/src/content/docs/self-hosting/logs.md index 79c89b3..b908125 100644 --- a/src/content/docs/self-hosting/logs.md +++ b/src/content/docs/self-hosting/logs.md @@ -1,19 +1,19 @@ --- -title: Logs and tracing -description: Read the server's logs and send them to OpenTelemetry. +title: Read logs and traces +description: Read the server's logs, and send logs and traces to OpenTelemetry. --- -The server logs to standard output, so `docker logs chek` shows them. Set the level with `RUST_LOG`, such as `RUST_LOG=debug`. +The server writes its logs to standard output. With Docker, run `docker logs chek` to read them. To change the level, set `RUST_LOG`, such as `RUST_LOG=debug`. The default is `info`. -Every request is logged with its method, route, path, status, duration, client address and user agent, inside a span named after its route, such as `GET /api/v1/publications/{publicationId}/pages/{pageIndex}`, so traces group by endpoint. +The server logs each request with its method, route, path, status, duration, client address, and user agent. Each request has a span named after its route, such as `GET /api/v1/publications/{publicationId}/pages/{pageIndex}`. Traces group by endpoint because of these names. -## OpenTelemetry +## Send logs and traces to OpenTelemetry -To send logs and traces to an OpenTelemetry collector over OTLP/HTTP: +Set the address of your OpenTelemetry collector. The server sends logs and traces to it over OTLP/HTTP. ```sh OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318 OTEL_SERVICE_NAME=chek ``` -The standard `OTEL_*` variables apply. The exporter doesn't use TLS, since the collector is expected on the same private network. Queued data is sent when the server shuts down. +The other standard `OTEL_*` variables apply too. The exporter doesn't use TLS, so run the collector on the same private network as the server. When the server shuts down, it sends the data in its queue. diff --git a/src/content/docs/self-hosting/opds.md b/src/content/docs/self-hosting/opds.md index fb99494..2187ab6 100644 --- a/src/content/docs/self-hosting/opds.md +++ b/src/content/docs/self-hosting/opds.md @@ -1,21 +1,27 @@ --- -title: OPDS -description: Browse and read Chek from OPDS 2 apps such as Thorium and KOReader. +title: Browse Chek from an OPDS reader +description: Add Chek to Thorium, KOReader, or another OPDS 2 reader. --- -Chek serves an [OPDS 2](https://drafts.opds.io/opds-2.0) catalog, so reading apps that speak OPDS can browse and download from it. +Chek serves an [OPDS 2](https://drafts.opds.io/opds-2.0) catalog. Reading apps that support OPDS 2 can browse it and download from it. -``` -https://chek.example.org/opds/v2/catalog -``` +1. In the reading app, add this catalog address: -Sign in with a Chek name and password. OPDS apps can't hold session tokens, so they send HTTP Basic credentials. When they don't, the catalog answers `401` with both a Basic challenge, which KOReader follows, and an [Authentication for OPDS](https://drafts.opds.io/authentication-for-opds-1.0) document, which Thorium needs. People who sign in only through a [provider](/self-hosting/sign-in-providers/) need to set a password first. + ``` + https://chek.example.org/opds/v2/catalog + ``` -## What's in it +2. Sign in with a Chek name and password. -- **The catalog** leads with Continue reading and Recently added, then lists the libraries you may open. -- **A library** lists its series, after links to All publications and Recently added. -- **A series** lists its publications in reading order, 50 to a page. -- **Search** is offered for the whole catalog and for each library. +OPDS apps can't keep a session token, so they sign in with HTTP Basic credentials. If you signed up through a [sign-in provider](/self-hosting/sign-in-providers/), set a password first. -Files and covers are served under `/opds/v2` too, so apps send the same credentials they used for the catalog. Publication metadata follows the Readium Web Publication model, so roles such as penciler and translator keep their own names. +When an app connects without credentials, Chek answers `401` with two kinds of challenge. KOReader follows the Basic challenge. Thorium needs the second one, an [Authentication for OPDS](https://drafts.opds.io/authentication-for-opds-1.0) document. + +## What the catalog contains + +- The catalog starts with Continue reading and Recently added. Then it lists the libraries you can open. +- A library lists its series, after links to All publications and Recently added. +- A series lists its publications in reading order, 50 on each page. +- The catalog and each library have a search link. + +Chek serves files and covers under `/opds/v2` too, so the app sends the same credentials for them. Publication metadata follows the Readium Web Publication model, so roles such as penciler and translator keep their own names. diff --git a/src/content/docs/self-hosting/reverse-proxy.md b/src/content/docs/self-hosting/reverse-proxy.md index 299d794..6f0dbfc 100644 --- a/src/content/docs/self-hosting/reverse-proxy.md +++ b/src/content/docs/self-hosting/reverse-proxy.md @@ -1,71 +1,75 @@ --- -title: HTTPS and reverse proxies -description: Put Chek server behind Caddy, nginx or a Cloudflare Tunnel, and tell it which proxy to trust. +title: Serve Chek over HTTPS +description: Put Chek server behind Caddy, nginx, or Cloudflare, and tell the server which proxy to trust. --- -Chek server speaks plain HTTP. Before you open it beyond your home network, put it behind a reverse proxy that serves HTTPS. Away from your local network, the Chek app and the [web app](https://app.chek.page) only reach servers over HTTPS. +Chek server speaks plain HTTP. Before the server is reachable from outside your home network, put a reverse proxy with HTTPS in front of it. Off your local network, the Chek app and the web app connect only to HTTPS servers. -## Trust your proxy +## Tell the server which proxy to trust -Behind a proxy, every request seems to come from the proxy's address. Chek limits wrong passwords per address, so without help everyone would share one limit. Tell the server which addresses are your proxy: +Behind a proxy, every request comes from the proxy's address. The server limits wrong passwords per address, so without this setting all your users share one limit. + +Set `CHEK_TRUSTED_PROXIES` to your proxy's addresses: ```sh CHEK_TRUSTED_PROXIES=172.16.0.0/12 ``` -On requests from those addresses, the client's address is read from `X-Forwarded-For`. Requests from anywhere else are taken at their own address, so clients can't pick an address by sending the header themselves. +The server then reads the client address from `X-Forwarded-For`, but only on requests from those addresses. A client that connects directly can't choose its address by sending the header. -For a proxy in another container on the same Docker network, the Docker ranges `10.0.0.0/8,172.16.0.0/12,192.168.0.0/16` cover it. +If the proxy runs in another container on the same Docker network, these ranges cover it: `10.0.0.0/8,172.16.0.0/12,192.168.0.0/16`. -## Caddy +## Use Caddy -Caddy gets and renews a certificate by itself and sends `X-Forwarded-For`. +Caddy gets and renews the certificate and sends `X-Forwarded-For` by default. ```caddy title="Caddyfile" chek.example.org { - reverse_proxy chek:8080 + reverse_proxy chek:8080 } ``` -## nginx +## Use nginx ```nginx server { - listen 443 ssl; - server_name chek.example.org; - # ssl_certificate and ssl_certificate_key as usual - - # Uploads can be large. Match CHEK_MAX_UPLOAD_BYTES if admins upload. - client_max_body_size 2g; - - location / { - proxy_pass http://127.0.0.1:8080; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - # Pages and files stream straight through. - proxy_buffering off; - } + listen 443 ssl; + server_name chek.example.org; + # Add ssl_certificate and ssl_certificate_key here. + + # Admins can upload files up to CHEK_MAX_UPLOAD_BYTES. + client_max_body_size 2g; + + location / { + proxy_pass http://127.0.0.1:8080; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + # Send pages and files to the client as they arrive. + proxy_buffering off; + } } ``` -With nginx on the host and Chek on `127.0.0.1`, set `CHEK_TRUSTED_PROXIES=127.0.0.1`. +If nginx runs on the host and Chek listens on `127.0.0.1`, set `CHEK_TRUSTED_PROXIES=127.0.0.1`. -## Cloudflare +## Use Cloudflare -Behind Cloudflare, whether through a Tunnel or a proxied DNS record, also set: +Behind Cloudflare, through a Tunnel or a proxied DNS record, also set this variable: ```sh CHEK_CLIENT_IP_HEADER=CF-Connecting-IP ``` -A tunnel or proxy in between may replace `X-Forwarded-For`, so the server reads the client from Cloudflare's own header instead. It reads the header only from `CHEK_TRUSTED_PROXIES`, so set that to your `cloudflared` container or proxy too. This only helps if every request comes through Cloudflare: a proxy that can be reached directly lets clients send their own value. +A tunnel can replace `X-Forwarded-For`, so the server reads the client address from Cloudflare's header instead. The server reads the header only on requests from `CHEK_TRUSTED_PROXIES`. Set that variable to the address of your `cloudflared` container or your proxy. + +The header is reliable only if every request comes through Cloudflare. If clients can reach your proxy directly, they can send any value in the header. -Cloudflare caps request bodies on its plans, 100 MB on Free and Pro, so larger uploads have to go around it. +Cloudflare limits the size of a request body. On the Free and Pro plans, the limit is 100 MB, so larger uploads must not go through Cloudflare. -## Checklist +## Check the setup -- HTTPS on a name you control, such as `chek.example.org`. -- `CHEK_TRUSTED_PROXIES` set to the proxy, and `CHEK_CLIENT_IP_HEADER` behind Cloudflare. -- `CHEK_PUBLIC_URL=https://chek.example.org` if you use [sign-in providers](/self-hosting/sign-in-providers/). -- The container's port not published to the internet directly. Use `expose` instead of `ports` in Compose. +- The server has HTTPS on a domain that you control, such as `chek.example.org`. +- `CHEK_TRUSTED_PROXIES` names the proxy. Behind Cloudflare, `CHEK_CLIENT_IP_HEADER` is set too. +- If you use [sign-in providers](/self-hosting/sign-in-providers/), `CHEK_PUBLIC_URL` is the HTTPS address, such as `https://chek.example.org`. +- Port `8080` isn't published to the internet. In Compose, the service uses `expose` instead of `ports`. diff --git a/src/content/docs/self-hosting/sign-in-providers.md b/src/content/docs/self-hosting/sign-in-providers.md index 31a8563..15b4092 100644 --- a/src/content/docs/self-hosting/sign-in-providers.md +++ b/src/content/docs/self-hosting/sign-in-providers.md @@ -1,60 +1,65 @@ --- -title: Sign-in providers -description: Let people sign in with OpenID Connect providers such as Authentik, Authelia, Keycloak, Pocket ID or Google. +title: Add sign-in providers +description: Let people sign in to Chek with OpenID Connect providers such as Authentik, Authelia, Keycloak, Pocket ID, or Google. --- -Besides a password, people can sign in through any OpenID Connect provider. Each provider is set up with environment variables. +People can sign in with a password or with an OpenID Connect provider. You set up each provider with environment variables. The [configuration reference](/self-hosting/configuration/#provider-settings) lists every provider setting. -## Set up +## Set up a provider -1. Set `CHEK_PUBLIC_URL` to the address people reach the server at, such as `https://chek.example.org`. -2. In your provider, create a client and register this redirect URI. The server also logs it at startup. +1. Set `CHEK_PUBLIC_URL` to the address that people use to reach the server, such as `https://chek.example.org`. +2. In your provider, create a client. Register this redirect URI for it: ``` https://chek.example.org/api/v1/auth/callback ``` -3. List your providers' ids, and give each one its settings. In variable names the id is upper-cased, with `-` written as `_`. + The server also writes this URI to its log when it starts. +3. Set `CHEK_OIDC_PROVIDERS` to an id for the provider, and set the provider's variables. In each name, write the id in upper case. + + ```sh + CHEK_PUBLIC_URL=https://chek.example.org + CHEK_OIDC_PROVIDERS=authentik + CHEK_OIDC_AUTHENTIK_ISSUER=https://auth.example.org/application/o/chek/ + CHEK_OIDC_AUTHENTIK_CLIENT_ID= + CHEK_OIDC_AUTHENTIK_CLIENT_SECRET= + CHEK_OIDC_AUTHENTIK_NAME=Authentik + ``` + +4. Restart the server. + +The server fetches the provider's discovery document the first time someone uses the provider. If the provider is down, the server still starts. + +## Create accounts at first sign-in + +By default, a person needs a Chek account before signing in with a provider. To create accounts for new people at their first sign-in, set `_CREATE_USERS`: ```sh -CHEK_PUBLIC_URL=https://chek.example.org -CHEK_OIDC_PROVIDERS=authentik -CHEK_OIDC_AUTHENTIK_ISSUER=https://auth.example.org/application/o/chek/ -CHEK_OIDC_AUTHENTIK_CLIENT_ID= -CHEK_OIDC_AUTHENTIK_CLIENT_SECRET= -CHEK_OIDC_AUTHENTIK_NAME=Authentik CHEK_OIDC_AUTHENTIK_CREATE_USERS=true CHEK_OIDC_AUTHENTIK_ADMIN_GROUPS=chek-admins ``` -| Setting | Default | What it does | -| --- | --- | --- | -| `_ISSUER` | required | The provider's issuer URL, over HTTPS (plain HTTP only on loopback, for testing) | -| `_CLIENT_ID` | required | The client's id | -| `_CLIENT_SECRET` | none | The client's secret. Leave it out for a public client | -| `_NAME` | the id | The label on the sign-in button | -| `_SCOPES` | `openid profile email` | Scopes to ask for | -| `_CREATE_USERS` | `false` | Whether someone unknown gets a new account | -| `_ADMIN_GROUPS` | none | Comma-separated groups whose members become admins when their account is created | -| `_GROUPS_CLAIM` | `groups` | The claim that lists a person's groups | +- The server takes the new account's name from `preferred_username`, `name`, or `email`. If the name is taken, it adds a number. +- A person in one of `_ADMIN_GROUPS` becomes an admin. Everyone else becomes a reader. +- A new reader has no libraries until an admin adds some. +- Later sign-ins don't change the role. -The server fetches each provider's discovery document the first time it's used, so a provider that's down never stops the server from starting. +The server matches people by the provider's subject id. It never matches by email or name. -## Who gets in +## Link a provider to an existing account -People are matched by the provider's subject id, never by email or name. +Without `_CREATE_USERS`, a person signs in with a password once. Then the person links the provider from their account. A person can't unlink a provider that is the account's only way to sign in. -- **With `_CREATE_USERS=true`**, someone the server doesn't know gets an account named from `preferred_username`, `name` or `email`, with a number added if the name is taken. They're an admin if they're in one of `_ADMIN_GROUPS` and a reader otherwise. Roles aren't changed on later sign-ins, and new readers start with no libraries until an admin grants some. -- **Otherwise**, people sign in with their password once and link the provider from their account. +An account that a provider created has no password. OPDS readers sign in with a password, so the account's owner must set one before using an OPDS reader. -Accounts made by a provider have no password. [OPDS readers](/self-hosting/opds/) sign in with a password, so those people set one first. A provider can't be unlinked while it's someone's only way to sign in. +## Allow your own app's redirect URI -## Apps +An app starts a sign-in with a PKCE challenge and its own redirect URI. The server sends back a one-time code, which only that app can exchange for a session. The redirect URI must start with `CHEK_PUBLIC_URL` or with a prefix in `CHEK_OIDC_REDIRECTS`. The default prefix is `chek://`, for the Chek app. -Apps start a sign-in with a PKCE challenge and a redirect URI of their own, and get back a one-time code that only they can trade for a session. Redirect URIs must start with `CHEK_PUBLIC_URL` or a prefix in `CHEK_OIDC_REDIRECTS`, which is `chek://` by default for the Chek app. If you build your own app, add its scheme: +If you build your own app, add its scheme: ```sh CHEK_OIDC_REDIRECTS=chek://,myapp:// ``` -Sign-ins in progress are kept in memory for 10 minutes, so restarting the server cancels them. +The server keeps sign-ins in progress in memory for 10 minutes. A restart cancels them. diff --git a/src/content/docs/self-hosting/troubleshooting.md b/src/content/docs/self-hosting/troubleshooting.md index aa28130..3e6ea87 100644 --- a/src/content/docs/self-hosting/troubleshooting.md +++ b/src/content/docs/self-hosting/troubleshooting.md @@ -1,45 +1,51 @@ --- title: Troubleshooting -description: Common problems running Chek server and what to do about them. +description: Fix common problems with Chek server. --- -## The server won't start +## The server doesn't start -It names the variable it couldn't read and why, such as `CHEK_SETUP_TOKEN: use at least 32 characters`. Fix that variable. `docker logs chek` shows the message. +Read the log with `docker logs chek`. If a setting is wrong, the log names the variable and the reason, such as `CHEK_SETUP_TOKEN: use at least 32 characters`. Fix the variable and start the server again. -## I can't find the setup token +## The setup token isn't in the log -It's logged only while the server has no accounts. Look further back in `docker logs chek`, or set your own with `CHEK_SETUP_TOKEN` and restart. Once an admin exists, setup answers `409 already_set_up`. +The server writes the token only while it has no accounts. Look further back in `docker logs chek`. Or set your own token in `CHEK_SETUP_TOKEN` and restart the server. + +If setup answers `409 already_set_up`, an admin account exists already. Sign in with it instead. ## A library is empty -- Check the path is the one **inside the container**, such as `/library/comics`, not the one on your host. -- Check the container can read it. The image runs as uid 10001, so the files need to be readable by that user or by everyone. -- Wait for the scan to finish. `GET /api/v1/libraries` shows each library's `scan` state. -- Files whose format the server can't tell are skipped. Supported formats are EPUB, PDF, CBZ, CBR, CB7, CBT, MOBI, AZW3, FB2, plain text and Markdown. +1. Check that the library's path is the path inside the container, such as `/library/comics`, not the path on the host. +2. Check that the container can read the files. The image runs as uid 10001, so that user or everyone needs read access. +3. Wait for the scan to finish. `GET /api/v1/libraries` shows the `scan` state of each library. + +The server skips files that aren't a format it reads. It reads EPUB, PDF, CBZ, CBR, CB7, CBT, MOBI, AZW3, FB2, plain text, and Markdown. ## A page or file answers `409 content_changed` -The file changed on disk since it was scanned, so its stored page offsets no longer match. The server has already started a rescan; try again when it's done. +The file changed on disk after the last scan, so the server's page offsets no longer match it. The server has already started a rescan. Try again after the scan. + +## Every user gets `429 too_many_attempts` -## Everyone gets `429 too_many_attempts` +The server sees all clients at your proxy's address. Set `CHEK_TRUSTED_PROXIES`, as [Tell the server which proxy to trust](/self-hosting/reverse-proxy/#tell-the-server-which-proxy-to-trust) describes. Behind Cloudflare, also set `CHEK_CLIENT_IP_HEADER`. -The server sees every client at your proxy's address. Set [`CHEK_TRUSTED_PROXIES`](/self-hosting/reverse-proxy/#trust-your-proxy), and `CHEK_CLIENT_IP_HEADER` behind Cloudflare. +## The app or the web app can't connect -## The app or web app can't connect +- Off your local network, the app needs an `https://` address. +- The web app at app.chek.page connects only to HTTPS servers. Its origin must be in `CHEK_WEB_ORIGINS`, which it is by default. +- On the device, open `https:///health`. It should show `{"status":"ok"}`. -- Away from your local network, the app needs an `https://` address. -- The web app at app.chek.page reaches only HTTPS servers, and only if its origin is in `CHEK_WEB_ORIGINS`. It is by default. -- Check `https:///health` answers from the device. +## A Komga or Kavita library isn't added -## A Komga or Kavita library won't add +When you add the library, the server checks the address and the credentials. If the check fails, it answers `400 source_check_failed` with the reason from Komga or Kavita. -Adding one checks the address and credentials first, and answers `400 source_check_failed` with Komga's or Kavita's reason. Check the URL is reachable from the Chek container, not just from your browser, and that the account has the [roles it needs](/self-hosting/komga-and-kavita/). +- Check that the Chek container can reach the URL. Your browser reaching it isn't enough. +- Check that the account has the roles that [Read Komga and Kavita libraries](/self-hosting/komga-and-kavita/) lists. ## An extension's site keeps failing -Some sites put a challenge in front of their pages. See [Sites behind a challenge](/self-hosting/extensions/#sites-behind-a-challenge). If verification passes but requests still fail, the site may be refusing your server's outbound address. +The site may show a browser challenge. Follow [Let extensions get past site challenges](/self-hosting/extensions/#let-extensions-get-past-site-challenges). If verification works but requests still fail, the site may block your server's outbound address. -## Still stuck +## Report a problem -Open an issue on [readchek/chek](https://github.com/readchek/chek/issues) with the server version, how you run it, and the log lines around the problem. Leave out tokens and passwords. +Open an issue on [readchek/chek](https://github.com/readchek/chek/issues). Include the server version, how you run the server, and the log lines around the problem. Remove tokens and passwords from the logs first. diff --git a/src/content/docs/self-hosting/updating-and-backups.md b/src/content/docs/self-hosting/updating-and-backups.md index edf3c79..d5c9129 100644 --- a/src/content/docs/self-hosting/updating-and-backups.md +++ b/src/content/docs/self-hosting/updating-and-backups.md @@ -1,51 +1,56 @@ --- -title: Updating and backups -description: Move between releases and keep a copy of what can't be rebuilt. +title: Update and back up the server +description: Move to a new release, and keep a copy of the data that you can't rebuild. --- -## Updating +## Update to a new release -Releases are tags on `main`, such as `0.3.0`, and each has an image of the same name. `latest` follows releases and `edge` follows `main`. +Each release is a tag on `main`, such as `0.3.0`, with an image of the same name. The `latest` tag follows releases, and `edge` follows `main`. Read the [release notes](https://github.com/readchek/chek/releases) before you update. + +To update with Docker, pull the image and run the container again with the same volumes: ```sh docker pull ghcr.io/readchek/chek:latest docker stop chek && docker rm chek -# Run the same `docker run` as before, with the same volumes. +# Run the same docker run command as before. ``` -With Compose, `docker compose pull && docker compose up -d`. With Dokploy, redeploy the application. +- With Compose, run `docker compose pull && docker compose up -d`. +- With Dokploy, deploy the application again. + +Keep the data volume. The server migrates the database when a new version starts. If a release changes how the server reads files, the migration starts a full rescan. So the first start after an update can scan every library again. -Keep the data volume. The database migrates itself when a new version starts. When a release reads files differently, the migration asks for a rescan, so the first start after an upgrade may scan every library again. +To stay on one release, use its tag, such as `ghcr.io/readchek/chek:0.3.0`. Change the tag when you are ready to update. -To stay on one release, use its tag, such as `ghcr.io/readchek/chek:0.3.0`, and change it when you're ready. Release notes are on [GitHub](https://github.com/readchek/chek/releases). +## Know what to back up -## What's in the data folder +The data folder holds these files. Your books stay in your own folders, which the server only reads. -| Path | What it is | If you lose it | +| Path | Contents | If you lose it | | --- | --- | --- | -| `chek.db` | Accounts, libraries, reading positions, bookmarks, highlights, metadata edits and settings, in SQLite | Gone for good. **Back this up** | -| `extensions/state.db` | Extensions' caches and cookie jars | Extensions start over; people may need to verify sites again | -| `extensions/*.wasm` | Installed extension components | Downloaded again on reinstall | -| `extensions/images/` | Cached covers and pages from extensions | Fetched again | -| `covers/` | Cover thumbnails | Made again on the next scan | +| `chek.db` | Accounts, libraries, reading positions, bookmarks, highlights, metadata edits, and settings | It's gone. Back it up | +| `extensions/state.db` | The caches and cookies of extensions | Extensions start again from empty. People may need to verify sites again | +| `extensions/*.wasm` | Installed extensions | The server downloads them again on the next install | +| `extensions/images/` | Cached covers and pages from extensions | The server fetches them again | +| `covers/` | Cover thumbnails | The next scan makes them again | -Your books stay in your own folders, which Chek only reads. +## Back up the database -## Backing up +The database uses SQLite's WAL mode. If you copy `chek.db` while the server writes to it, the copy can be incomplete. Use one of these two ways. -The database runs in SQLite's WAL mode, so copying `chek.db` while the server runs can catch it halfway through a write. Either stop the server and copy the folder: +To back up with the server stopped, stop it and copy the files: ```sh docker stop chek docker run --rm -v chek-data:/data -v "$PWD":/backup debian:trixie-slim \ - tar czf /backup/chek-data.tgz -C /data chek.db extensions/state.db + tar czf /backup/chek-data.tgz -C /data chek.db extensions/state.db docker start chek ``` -Or take a consistent copy while it runs with the `sqlite3` tool: +To back up while the server runs, use the `sqlite3` tool on the host: ```sh sqlite3 /path/to/data/chek.db ".backup '/backups/chek.db'" ``` -To restore, put `chek.db` back in an empty data folder before the server starts. +To restore, put `chek.db` in an empty data folder before the server starts. diff --git a/src/content/docs/self-hosting/uploads.md b/src/content/docs/self-hosting/uploads.md index 09630c6..d7ea882 100644 --- a/src/content/docs/self-hosting/uploads.md +++ b/src/content/docs/self-hosting/uploads.md @@ -1,21 +1,29 @@ --- -title: Uploads -description: Let admins add files to a folder library over HTTP. +title: Upload files +description: Add a file to a folder library over HTTP. --- -Admins can upload a file into a folder library by path: +An admin can upload a file into a folder library. The library's folder must be mounted read-write. ```sh curl -X PUT "$SERVER/api/v1/libraries/1/files/Saga/Saga%20001.cbz" \ - -H "Authorization: Bearer $TOKEN" \ - -H 'content-type: application/octet-stream' \ - --data-binary @"Saga 001.cbz" + -H "Authorization: Bearer $TOKEN" \ + -H 'content-type: application/octet-stream' \ + --data-binary @"Saga 001.cbz" ``` -- Files must end in `.epub`, `.pdf`, `.cbz`, `.cbr`, `.cb7` or `.cbt`. Missing folders on the path are created. -- The library's folder must be mounted read-write. Otherwise uploads answer `409 library_read_only`. -- `CHEK_MAX_UPLOAD_BYTES` caps each file, 2 GiB by default; larger files answer `413`. Raise your [proxy's body limit](/self-hosting/reverse-proxy/) to match. -- The file streams to a hidden temporary file beside its destination, so a scan never sees half a file. It must match its extension, and is then moved into place and added to the library. -- Uploads never replace a file. A path that's taken answers `409 file_exists`. -- Every part of the path must be a plain name, with no `.`, `..` or names starting with `.`. Folders that lead outside it through a link are refused. -- If a scan of the library is running, the upload waits up to 20 seconds for it, then answers `202` and finishes recording the file in the background. +The path after `files/` is where the file goes, relative to the library's folder. The server creates any folder in the path that doesn't exist. + +## Rules for uploads + +- The file must end in `.epub`, `.pdf`, `.cbz`, `.cbr`, `.cb7`, or `.cbt`, and its contents must match that format. +- Each part of the path must be a plain name. The server refuses `.`, `..`, names that start with `.`, and folders that link outside the library. +- An upload never replaces a file. If a file is already at the path, the server answers `409 file_exists`. +- If the server can't write to the folder, it answers `409 library_read_only`. +- `CHEK_MAX_UPLOAD_BYTES` limits each file. The default is 2 GiB. A larger file answers `413`. Set your [proxy's body limit](/self-hosting/reverse-proxy/#use-nginx) to the same size. + +## What happens to the file + +The server writes the upload to a hidden temporary file next to its destination, so a scan never reads a partial file. When the upload is complete, the server checks the format, moves the file into place, and adds it to the library. + +If a scan of the library is running, the server waits up to 20 seconds for it to finish. After 20 seconds, it answers `202` and adds the file when the scan ends. diff --git a/src/content/docs/self-hosting/users.md b/src/content/docs/self-hosting/users.md index 3c2e327..aa37422 100644 --- a/src/content/docs/self-hosting/users.md +++ b/src/content/docs/self-hosting/users.md @@ -1,42 +1,55 @@ --- -title: Users and access -description: Roles, library grants, sessions, sign-in limits and demo accounts. +title: Accounts and access +description: Roles, library grants, sessions, sign-in limits, Basic credentials, and demo accounts. --- -## Roles and libraries +## Roles -- **Admins** add and change libraries and accounts, and see every library. -- **Readers** see only the libraries an admin granted them. Other libraries, and the series and publications in them, answer `404`, so a reader can't tell they exist. +| Role | Can do | +| --- | --- | +| `admin` | Add and change libraries and accounts. See every library | +| `reader` | See the libraries in the account's `libraryIds` | -Create accounts with `POST /api/v1/users`, and change a role, password or grants with `PATCH /api/v1/users/{userId}`. Names are 1 to 64 characters and not case-sensitive at sign-in. Passwords are at least 8 characters. +A library outside a reader's `libraryIds` answers `404`. So do its series and publications. -The server always keeps one admin. Demoting or deleting the last one answers `409 last_admin`. +The server always keeps one admin. A request to demote or delete the last admin answers `409 last_admin`. -## Sessions +## Names and passwords + +- A name is 1 to 64 characters. Sign-in ignores case in names. +- A password is at least 8 characters and at most 1024 bytes. +- The server hashes passwords with Argon2id. -- Signing in returns a random session token. The server stores only its hash. -- A session lasts 90 days after it was last used. -- People can list their own signed-in devices with `GET /api/v1/me/sessions` and sign any of them out. -- Changing a password signs out the account's other sessions. +## Sessions -Passwords are hashed with Argon2id. +- Sign-in returns a random session token. The server stores only a hash of the token. +- A session ends 90 days after its last use. +- `GET /api/v1/me/sessions` lists a person's sessions. `DELETE /api/v1/me/sessions/{sessionId}` ends one. +- A password change ends the account's other sessions. -## Wrong passwords +## Limits on wrong passwords -After 10 wrong passwords from one address within 15 minutes, password checks from that address answer `429 too_many_attempts` until the oldest failure is 15 minutes old. The limit is per address, not per account, so nobody can lock someone else out by guessing their name. Signed-in sessions aren't affected. +After 10 wrong passwords from one address in 15 minutes, password checks from that address answer `429 too_many_attempts`. The block lifts when the oldest failure is 15 minutes old. -Behind a reverse proxy, set [`CHEK_TRUSTED_PROXIES`](/self-hosting/reverse-proxy/) or every client shares the proxy's limit. +- The limit counts per address, not per account. A stranger can't lock a person out by guessing that person's name. +- The limit doesn't affect session tokens. +- Behind a reverse proxy, the limit needs `CHEK_TRUSTED_PROXIES`. Without it, every client shares the proxy's limit. Read [Tell the server which proxy to trust](/self-hosting/reverse-proxy/#tell-the-server-which-proxy-to-trust). ## Basic credentials -Requests may send HTTP Basic credentials, a name and password, instead of a session token. [OPDS readers](/self-hosting/opds/) need this, since they can't hold a token. Accounts made by a [sign-in provider](/self-hosting/sign-in-providers/) have no password until their owner sets one. +A request can send HTTP Basic credentials, a name and a password, instead of a session token. OPDS readers need Basic credentials, because they can't keep a token. An account that a [sign-in provider](/self-hosting/sign-in-providers/) created has no password until its owner sets one. ## Demo accounts -`CHEK_DEMO_USERS` names accounts to treat as shared demos, whose password you publish. Create the accounts first, then list their names: +`CHEK_DEMO_USERS` lists the names of accounts that the server treats as shared demos. Create the accounts first. Then list their names: ```sh CHEK_DEMO_USERS=demo ``` -Demo accounts can't change their password, sessions, sign-in providers, extensions, repositories or annotations; those answer `403 demo_account`. Their reading positions answer as saved but aren't kept, and they see only their own session, so visitors never see each other's places or devices. They see Safe and Suggestive content only. +A demo account has these limits: + +- Changes to its password, sessions, sign-in providers, extensions, repositories, or annotations answer `403 demo_account`. +- A saved reading position answers as saved, but the server doesn't keep it. +- It sees only its own session, so one visitor never sees another visitor's device. +- It sees only Safe and Suggestive content.