diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index b66f5a00..3e02d286 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,63 +1,66 @@ name: Documentation - on: push: branches: [main] - paths: - - 'docs/**' - - 'docs.json' - - 'llms.txt' - # No `branches:` filter. A stacked pull request targets its parent - # branch, not main, and would otherwise run no checks at all. + paths: ['docs/**', 'docs-site/**', '.github/workflows/docs.yml', 'llms.txt'] pull_request: - paths: - - 'docs/**' - - 'docs.json' - - 'llms.txt' - + paths: ['docs/**', 'docs-site/**', '.github/workflows/docs.yml', 'docs.json', 'llms.txt'] permissions: contents: read - concurrency: group: docs-${{ github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} - jobs: validate: name: validate runs-on: ubuntu-24.04 - timeout-minutes: 15 + timeout-minutes: 20 + env: + DOCS_BASE_PATH: /ack + NEXT_PUBLIC_SITE_URL: https://concepta.dev/ack + NEXT_TELEMETRY_DISABLED: '1' + defaults: + run: + working-directory: docs-site steps: - name: Checkout repository - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 with: persist-credentials: false - - name: Set up Node.js - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 with: - node-version: '22' - - - name: Verify llms.txt sources - run: | - set -euo pipefail - test -s llms.txt || { - echo "llms.txt is missing or empty." - exit 1 - } - test ! -e docs/llms.txt.mdx || { - echo "docs.page reserves /llms.txt; remove docs/llms.txt.mdx to avoid a duplicate index entry." - exit 1 - } - grep -q 'https://docs.page/conceptadev/ack/llms.txt' docs/index.mdx || { - echo "The docs homepage must link to the generated docs.page llms.txt export." - exit 1 - } - - # Pin the CLI version. An unpinned `npm install -g @docs.page/cli` would - # execute whatever the registry serves at run time, which is the - # mutable-dependency problem that the other workflows removed. - - name: Validate documentation + node-version: '24.14.0' + - name: Install pinned pnpm + run: npm install --global pnpm@11.5.3 + - name: Install documentation dependencies + run: pnpm install --frozen-lockfile + - name: Check helper contracts and dependency identity + run: pnpm test + - name: Type-check documentation + run: pnpm typecheck + - name: Verify the canonical content mirror + run: diff -qr ../docs content/docs + - name: Install the pinned browser test runtime + run: pnpm exec playwright install --with-deps chromium + - name: Build and verify a root deployment env: - DOCS_PAGE_CLI_VERSION: '2.0.0' - run: npx --yes "@docs.page/cli@$DOCS_PAGE_CLI_VERSION" check + DOCS_BASE_PATH: '' + NEXT_PUBLIC_SITE_URL: https://docs.example.test + run: | + pnpm build + python3 scripts/verify-export.py + pnpm test:browser + - name: Build and verify the production subpath + run: | + pnpm build + python3 scripts/verify-export.py + pnpm test:browser + - name: Check runtime dependency advisories + run: pnpm audit --prod --audit-level=high + - name: Check migration invariants + run: | + test ! -e ../docs.json + test ! -e theme + test ! -e app/docs + test ! -e scripts/sync-content.mjs diff --git a/.gitignore b/.gitignore index f98ee3c0..53f97f5c 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,13 @@ CLAUDE.md tools/node_modules/ tools/package-lock.json +# Fumadocs application outputs +docs-site/node_modules/ +docs-site/.next/ +docs-site/.source/ +docs-site/content/ +docs-site/out/ + # Temporary test files temp/ *.tmp diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7ae7eba3..27a68d41 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,6 +14,34 @@ dart run melos bootstrap Ack uses a Melos workspace. Run commands from the repository root unless a package README says otherwise. +## Documentation changes + +The canonical documentation source is in `docs/`. Do not edit +`docs-site/content`; the Fumadocs application generates that mirror for local +and production builds. + +Set up and run the documentation site with: + +```bash +cd docs-site +npm install --global pnpm@11.5.3 +pnpm install --frozen-lockfile +pnpm dev +``` + +The development server watches `docs/` and refreshes the content mirror. Open +`http://localhost:3000`. + +Before submitting documentation changes, run: + +```bash +cd docs-site +pnpm typecheck +DOCS_BASE_PATH=/ack \ +NEXT_PUBLIC_SITE_URL=https://concepta.dev/ack \ +pnpm build +``` + ## Before opening a PR 1. Keep the change scoped to one problem. diff --git a/README.md b/README.md index dfd0f8f5..298b5148 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,11 @@ [![CI/CD](https://github.com/conceptadev/ack/actions/workflows/ci.yml/badge.svg)](https://github.com/conceptadev/ack/actions/workflows/ci.yml) [![Documentation](https://img.shields.io/badge/docs-documentation-blue)](https://concepta.dev/ack) [![pub package](https://img.shields.io/pub/v/ack.svg)](https://pub.dev/packages/ack) -[![llms.txt](https://img.shields.io/badge/llms.txt-available-8A2BE2)](https://concepta.dev/documentation/ack/reference/llms-txt) +[![llms.txt](https://img.shields.io/badge/llms.txt-available-8A2BE2)](https://concepta.dev/ack/llms.txt) Ack is a schema validation library for Dart and Flutter. It validates data with a fluent API. Ack is short for "acknowledge". -For AI agents: start at [`/llms.txt`](https://concepta.dev/documentation/ack/reference/llms-txt). +For AI agents: start at [`/llms.txt`](https://concepta.dev/ack/llms.txt). ## Why use Ack? @@ -234,12 +234,12 @@ csv.encode(['a', 'b', 'c']); // 'a,b,c' ``` Use `.transform(...)` for one-way (parse-only) conversions. See the -[Codecs guide](https://concepta.dev/documentation/ack/advanced/codecs). +[Codecs guide](https://concepta.dev/ack/core-concepts/codecs). ## Documentation - Human docs: [concepta.dev/ack](https://concepta.dev/ack) -- AI agent index: [AI & llms.txt](https://concepta.dev/documentation/ack/reference/llms-txt) +- AI agent index: [llms.txt](https://concepta.dev/ack/llms.txt) - Canonical plaintext source: [raw.githubusercontent.com/conceptadev/ack/main/llms.txt](https://raw.githubusercontent.com/conceptadev/ack/main/llms.txt) ## Development diff --git a/SUPPORT.md b/SUPPORT.md index 0b25c488..91e6df54 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -3,8 +3,8 @@ ## Start with the documentation - [Ack documentation](https://concepta.dev/ack) -- [Quickstart tutorial](https://concepta.dev/documentation/ack/getting-started/quickstart-tutorial) -- [API quick reference](https://concepta.dev/documentation/ack/reference/api-reference) +- [Quickstart tutorial](https://concepta.dev/ack/getting-started/quickstart-tutorial) +- [API quick reference](https://concepta.dev/ack/api-reference/) - [Generated API documentation](https://pub.dev/documentation/ack/latest/ack/) Search existing [GitHub issues](https://github.com/conceptadev/ack/issues) before diff --git a/docs-site/.gitignore b/docs-site/.gitignore new file mode 100644 index 00000000..49bc7bd8 --- /dev/null +++ b/docs-site/.gitignore @@ -0,0 +1,8 @@ +.next/ +.source/ +node_modules/ +out/ +content/ +playwright-report/ +test-results/ +*.tsbuildinfo diff --git a/docs-site/DEPLOYMENT.md b/docs-site/DEPLOYMENT.md new file mode 100644 index 00000000..bfea7b8c --- /dev/null +++ b/docs-site/DEPLOYMENT.md @@ -0,0 +1,50 @@ +# Documentation deployment + +The app exports static files to `docs-site/out`. It has one route contract: +application-relative `/core-concepts/schemas` is published at +`https://concepta.dev/ack/core-concepts/schemas`. + +## Build + +```bash +cd docs-site +npm install --global pnpm@11.5.3 +pnpm install --frozen-lockfile +DOCS_BASE_PATH=/ack NEXT_PUBLIC_SITE_URL=https://concepta.dev/ack pnpm build +DOCS_BASE_PATH=/ack NEXT_PUBLIC_SITE_URL=https://concepta.dev/ack python3 scripts/verify-export.py +DOCS_BASE_PATH=/ack pnpm test:browser +``` + +Install the Playwright Chromium runtime before running browser tests. Publish +`out` at the `/ack` mount, not at `/ack/docs`. The host must serve directory +indexes and raw files, including `/ack/api/search`, `/ack/llms.txt`, Markdown +files, and PNG images. Do not use a homepage fallback for missing files. + +Next.js adds basePath to its links. Raw fetches use `addBasePath`; canonical, +sitemap, image metadata, and LLM URLs use `createSiteUrl`. Do not prefix a URL +twice. `site.url` includes the deployment path, while `site.docsPath` is `/`. + +## Crawler ownership + +The exported `/ack/robots.txt` is not a domain-wide crawler policy. Crawlers +read the origin's `/robots.txt`. Coordinate the Concepta root site's robots +configuration and add the sitemap `https://concepta.dev/ack/sitemap.xml` there. +Do not overwrite the root site's existing crawler rules. Review dates are not +used as sitemap modification dates. + +## Cutover gate + +This PR removes docs.page's `docs.json`. Do not merge until the new host can +serve the complete static export and the production route has been reviewed. +No deployment, DNS change, registry publication, or merge is automated here. + +Keep the previous deployment available for rollback. Verify existing public +links, search, Markdown copy, source links, images, navigation, and 404s at the +real host before completing cutover. Older `/documentation/ack` URLs require +explicit redirects at that host; this static app does not own that prefix. + +## Shared package follow-up + +After a reviewed theme release is published, replace the `workspace:*` +dependency, remove `packages/docs-theme`, regenerate the lockfile, and rerun the +same root/subpath checks. Do not retain two packages with the same name. diff --git a/docs-site/README.md b/docs-site/README.md new file mode 100644 index 00000000..e7e45b85 --- /dev/null +++ b/docs-site/README.md @@ -0,0 +1,62 @@ +# Ack documentation site + +This is the Fumadocs application for Ack. The canonical content is `../docs`. +Builds copy that directory byte-for-byte into ignored `content/docs`; edit only +`../docs`. This PR migrates the existing published documentation. It does not +automatically import package READMEs, the root README, or unrelated folders. + +## Development + +Use Node.js 24.14 or newer, pnpm 11.5.3, and Python 3 for static preview/checks. + +```bash +cd docs-site +npm install --global pnpm@11.5.3 +pnpm install --frozen-lockfile +pnpm dev +``` + +Open `http://localhost:3000`. The development process watches `../docs`. +The source loader, navigation, and pages all use application-root routes, not +an additional `/docs` prefix. Existing page paths remain unchanged. + +## Validate and preview + +```bash +pnpm test +pnpm typecheck +DOCS_BASE_PATH=/ack NEXT_PUBLIC_SITE_URL=https://concepta.dev/ack pnpm build +DOCS_BASE_PATH=/ack NEXT_PUBLIC_SITE_URL=https://concepta.dev/ack python3 scripts/verify-export.py +DOCS_BASE_PATH=/ack pnpm preview +``` + +Open `http://127.0.0.1:4173/ack/` for the exported preview. This is a static site; +`next start` is not its serving command. Production builds require a public +`NEXT_PUBLIC_SITE_URL` whose pathname matches `DOCS_BASE_PATH`. + +Browser checks: + +```bash +pnpm exec playwright install chromium +DOCS_BASE_PATH=/ack pnpm test:browser +``` + +CI tests both root and `/ack` deployments, desktop and mobile rendering, +keyboard search, Markdown and image endpoints, theme switching, missing pages, +internal links/fragments, sitemap URLs, and the Base UI dependency identity. + +## Theme ownership + +`packages/docs-theme` is the only local snapshot of `@conceptadev/docs-theme`. +See its `SOURCE.md`. It shares the reviewed public API with the theme repository: +`Status` takes `status`; page metadata takes `imageUrl`; source links use +`createSourceUrl`. The explicit Base UI alias and workspace peer policy prevent +a second Radix implementation. A published package can replace the snapshot +after the same validation passes; registry publishing is a separate decision. + +Optional front matter uses one shared `PAGE_STATUSES` enum. `lastReviewed` is +an editorial review date, not a content modification time. `draft` is only a +label; it does not hide pages from search or Markdown exports. + +See `DEPLOYMENT.md` for the hosting cutover requirement. This PR does not deploy +or change DNS, but deleting `docs.json` requires a coordinated migration. diff --git a/docs-site/app/(docs)/[[...slug]]/page.tsx b/docs-site/app/(docs)/[[...slug]]/page.tsx new file mode 100644 index 00000000..7c84630e --- /dev/null +++ b/docs-site/app/(docs)/[[...slug]]/page.tsx @@ -0,0 +1,40 @@ +import { createPageMetadata, createSourceUrl, Status } from '@conceptadev/docs-theme'; +import { createRelativeLink } from 'fumadocs-ui/mdx'; +import { DocsBody, DocsDescription, DocsPage, DocsTitle, MarkdownCopyButton, ViewOptionsPopover } from 'fumadocs-ui/layouts/docs/page'; +import type { Metadata } from 'next'; +import { notFound } from 'next/navigation'; +import { getMDXComponents } from '@/components/mdx'; +import { docsConfig } from '@/docs.config'; +import { getPageImageUrl, getPageMarkdownUrl, source } from '@/lib/source'; +import { withBasePath } from '@/lib/routes'; + +interface PageProps { params: Promise<{ slug?: string[] }> } +export const dynamicParams = false; + +export default async function Page({ params }: PageProps) { + const { slug } = await params; + const page = source.getPage(slug); + if (!page) notFound(); + const MDX = page.data.body; + const markdownUrl = withBasePath(getPageMarkdownUrl(page).url); + return ( + + {page.data.title} + {page.data.status ? : null} + {page.data.description} +
+ + +
+ + {page.data.lastReviewed ?

Last reviewed:

: null} +
+ ); +} +export function generateStaticParams() { return source.generateParams(); } +export async function generateMetadata({ params }: PageProps): Promise { + const { slug } = await params; + const page = source.getPage(slug); + if (!page) notFound(); + return createPageMetadata(docsConfig, { title: page.data.title, description: page.data.description, path: page.url, imageUrl: getPageImageUrl(page).url }); +} diff --git a/docs-site/app/(docs)/layout.tsx b/docs-site/app/(docs)/layout.tsx new file mode 100644 index 00000000..dc852993 --- /dev/null +++ b/docs-site/app/(docs)/layout.tsx @@ -0,0 +1,16 @@ +import type { ReactNode } from 'react'; +import { DocsLayout } from 'fumadocs-ui/layouts/docs'; +import { baseOptions } from '@/lib/layout.shared'; +import { source } from '@/lib/source'; + +export default function DocumentationLayout({ + children, +}: { + children: ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/docs-site/app/api/search/route.ts b/docs-site/app/api/search/route.ts new file mode 100644 index 00000000..14f3c258 --- /dev/null +++ b/docs-site/app/api/search/route.ts @@ -0,0 +1,6 @@ +import { createFromSource } from 'fumadocs-core/search/server'; +import { source } from '@/lib/source'; + +export const dynamic = 'force-static'; +export const revalidate = false; +export const { staticGET: GET } = createFromSource(source, { language: 'english' }); diff --git a/docs-site/app/global.css b/docs-site/app/global.css new file mode 100644 index 00000000..75bac4d7 --- /dev/null +++ b/docs-site/app/global.css @@ -0,0 +1,16 @@ +@import 'tailwindcss'; +@import 'fumadocs-ui/css/neutral.css'; +@import 'fumadocs-ui/css/preset.css'; +@import '@conceptadev/docs-theme/theme.css'; + +@source '../packages/docs-theme/src'; +@source '../content/docs'; + +html { + scrollbar-gutter: stable; +} + +html > body[data-scroll-locked] { + margin-right: 0 !important; + --removed-body-scroll-bar-size: 0px !important; +} diff --git a/docs-site/app/icon.svg b/docs-site/app/icon.svg new file mode 100644 index 00000000..6145dfea --- /dev/null +++ b/docs-site/app/icon.svg @@ -0,0 +1,7 @@ + + + diff --git a/docs-site/app/layout.tsx b/docs-site/app/layout.tsx new file mode 100644 index 00000000..9d399beb --- /dev/null +++ b/docs-site/app/layout.tsx @@ -0,0 +1,17 @@ +import { createSiteMetadata } from '@conceptadev/docs-theme'; +import type { ReactNode } from 'react'; +import { Provider } from '@/components/provider'; +import { docsConfig } from '@/docs.config'; +import './global.css'; + +export const metadata = createSiteMetadata(docsConfig); + +export default function RootLayout({ children }: { children: ReactNode }) { + return ( + + + {children} + + + ); +} diff --git a/docs-site/app/llms-full.txt/route.ts b/docs-site/app/llms-full.txt/route.ts new file mode 100644 index 00000000..7c786843 --- /dev/null +++ b/docs-site/app/llms-full.txt/route.ts @@ -0,0 +1,7 @@ +import { getLLMText, source } from '@/lib/source'; +export const dynamic = 'force-static'; +export const revalidate = false; +export async function GET() { + const pages = await Promise.all(source.getPages().map(getLLMText)); + return new Response(pages.join('\n\n'), { headers: { 'Content-Type': 'text/plain; charset=utf-8' } }); +} diff --git a/docs-site/app/llms.mdx/[[...slug]]/route.ts b/docs-site/app/llms.mdx/[[...slug]]/route.ts new file mode 100644 index 00000000..0f4f4ba8 --- /dev/null +++ b/docs-site/app/llms.mdx/[[...slug]]/route.ts @@ -0,0 +1,16 @@ +import { notFound } from 'next/navigation'; +import { getLLMText, getPageMarkdownUrl, source } from '@/lib/source'; + +export const dynamic = 'force-static'; +export const dynamicParams = false; +export const revalidate = false; +export async function GET(_request: Request, { params }: { params: Promise<{ slug?: string[] }> }) { + const { slug } = await params; + if (slug?.at(-1) !== 'content.md') notFound(); + const page = source.getPage(slug.slice(0, -1)); + if (!page) notFound(); + return new Response(await getLLMText(page), { headers: { 'Content-Type': 'text/markdown; charset=utf-8' } }); +} +export function generateStaticParams() { + return source.getPages().map((page) => ({ slug: getPageMarkdownUrl(page).segments })); +} diff --git a/docs-site/app/llms.txt/route.ts b/docs-site/app/llms.txt/route.ts new file mode 100644 index 00000000..6cbe7798 --- /dev/null +++ b/docs-site/app/llms.txt/route.ts @@ -0,0 +1,11 @@ +import { llms } from 'fumadocs-core/source'; +import { absoluteSiteUrl } from '@/lib/routes'; +import { source } from '@/lib/source'; + +export const dynamic = 'force-static'; +export const revalidate = false; +export function GET() { + // This rewrites generated URLs only; authored MDX is never rewritten by regex. + const index = llms(source).index().replace(/\]\((\/[^)]*)\)/g, (_match: string, path: string) => `](${absoluteSiteUrl(path)})`); + return new Response(index, { headers: { 'Content-Type': 'text/plain; charset=utf-8' } }); +} diff --git a/docs-site/app/og/[...slug]/route.tsx b/docs-site/app/og/[...slug]/route.tsx new file mode 100644 index 00000000..48a16a97 --- /dev/null +++ b/docs-site/app/og/[...slug]/route.tsx @@ -0,0 +1,18 @@ +import { generate as DefaultImage } from 'fumadocs-ui/og'; +import { notFound } from 'next/navigation'; +import { ImageResponse } from 'next/og'; +import { getPageImageUrl, source } from '@/lib/source'; + +export const dynamic = 'force-static'; +export const dynamicParams = false; +export const revalidate = false; +export async function GET(_request: Request, { params }: { params: Promise<{ slug: string[] }> }) { + const { slug } = await params; + if (slug.at(-1) !== 'image.png') notFound(); + const page = source.getPage(slug.slice(0, -1)); + if (!page) notFound(); + return new ImageResponse(, { width: 1200, height: 630 }); +} +export function generateStaticParams() { + return source.getPages().map((page) => ({ slug: getPageImageUrl(page).segments })); +} diff --git a/docs-site/app/robots.ts b/docs-site/app/robots.ts new file mode 100644 index 00000000..4aa55eb3 --- /dev/null +++ b/docs-site/app/robots.ts @@ -0,0 +1,10 @@ +import type { MetadataRoute } from 'next'; +import { absoluteSiteUrl } from '@/lib/routes'; + +export const dynamic = 'force-static'; +export default function robots(): MetadataRoute.Robots { + return { + rules: { userAgent: '*', allow: '/' }, + sitemap: absoluteSiteUrl('/sitemap.xml'), + }; +} diff --git a/docs-site/app/sitemap.ts b/docs-site/app/sitemap.ts new file mode 100644 index 00000000..ce138a9e --- /dev/null +++ b/docs-site/app/sitemap.ts @@ -0,0 +1,9 @@ +import type { MetadataRoute } from 'next'; +import { absoluteSiteUrl } from '@/lib/routes'; +import { source } from '@/lib/source'; + +export const dynamic = 'force-static'; +export default function sitemap(): MetadataRoute.Sitemap { + // lastReviewed is an editorial date, not a content modification timestamp. + return source.getPages().map((page) => ({ url: absoluteSiteUrl(page.url) })); +} diff --git a/docs-site/components/mdx.tsx b/docs-site/components/mdx.tsx new file mode 100644 index 00000000..9a46ba96 --- /dev/null +++ b/docs-site/components/mdx.tsx @@ -0,0 +1,10 @@ +import { getConceptaMDXComponents } from '@conceptadev/docs-theme/mdx'; +import type { MDXComponents } from 'mdx/types'; + +export function getMDXComponents(components?: MDXComponents) { + return getConceptaMDXComponents(components); +} +export const useMDXComponents = getMDXComponents; +declare global { + type MDXProvidedComponents = ReturnType; +} diff --git a/docs-site/components/provider.tsx b/docs-site/components/provider.tsx new file mode 100644 index 00000000..2ffdc5f4 --- /dev/null +++ b/docs-site/components/provider.tsx @@ -0,0 +1,9 @@ +'use client'; + +import { RootProvider } from 'fumadocs-ui/provider/next'; +import type { ReactNode } from 'react'; +import SearchDialog from '@/components/search'; + +export function Provider({ children }: { children: ReactNode }) { + return {children}; +} diff --git a/docs-site/components/search.tsx b/docs-site/components/search.tsx new file mode 100644 index 00000000..2cd59d1e --- /dev/null +++ b/docs-site/components/search.tsx @@ -0,0 +1,48 @@ +'use client'; + +import { useDocsSearch } from 'fumadocs-core/search/client'; +import { staticClient } from 'fumadocs-core/search/client/orama-static'; +import { + SearchDialog, + SearchDialogClose, + SearchDialogContent, + SearchDialogHeader, + SearchDialogIcon, + SearchDialogInput, + SearchDialogList, + SearchDialogOverlay, + type SharedProps, +} from 'fumadocs-ui/components/dialog/search'; +import { useI18n } from 'fumadocs-ui/contexts/i18n'; +import { withBasePath } from '@/lib/routes'; + +export default function DefaultSearchDialog(props: SharedProps) { + const { locale } = useI18n(); + const { search, setSearch, query } = useDocsSearch({ + client: staticClient({ + from: withBasePath('/api/search'), + locale, + }), + }); + + return ( + + + + + + + + + + + + ); +} diff --git a/docs-site/components/theme-contract.tsx b/docs-site/components/theme-contract.tsx new file mode 100644 index 00000000..863d41b8 --- /dev/null +++ b/docs-site/components/theme-contract.tsx @@ -0,0 +1,15 @@ +import { Status, createPageMetadata, type DocsConfig } from '@conceptadev/docs-theme'; +import { getConceptaMDXComponents } from '@conceptadev/docs-theme/mdx'; +import type { ComponentProps } from 'react'; + +export function checkThemeContract(config: DocsConfig) { + const components = getConceptaMDXComponents(); + const props: ComponentProps = { status: 'stable' }; + const valid = ; + // @ts-expect-error Status uses status, not the abandoned value prop. + const invalid = ; + createPageMetadata(config, { title: 'Guide', path: '/guide', imageUrl: '/og/guide/image.png' }); + // @ts-expect-error Metadata uses imageUrl, not image. + createPageMetadata(config, { title: 'Guide', path: '/guide', image: '/og/image.png' }); + return { valid, invalid }; +} diff --git a/docs-site/docs.config.ts b/docs-site/docs.config.ts new file mode 100644 index 00000000..52f9c566 --- /dev/null +++ b/docs-site/docs.config.ts @@ -0,0 +1,56 @@ +import { defineDocsConfig } from '@conceptadev/docs-theme'; + +const siteUrl = + process.env.NEXT_PUBLIC_SITE_URL ?? 'http://localhost:3000'; + +export const docsConfig = defineDocsConfig({ + organization: { + name: 'Concepta', + homeUrl: 'https://concepta.dev', + }, + site: { + title: 'Ack Documentation', + description: + 'Schema validation, codecs, and type-safe model generation for Dart and Flutter.', + url: siteUrl, + docsPath: '/', + }, + project: { + id: 'ack', + name: 'Ack', + description: + 'Schema validation for Dart and Flutter with actionable errors and optional code generation.', + docsUrl: '/', + repository: { + url: 'https://github.com/conceptadev/ack', + branch: 'main', + contentPath: 'docs', + }, + }, + projects: [ + { + id: 'ack', + name: 'Ack', + description: 'Schema validation for Dart and Flutter.', + docsUrl: '/', + }, + { + id: 'mix', + name: 'Mix', + description: 'A composable styling system for Flutter.', + docsUrl: 'https://www.fluttermix.com', + }, + ], + socialLinks: [ + { + label: 'pub.dev', + url: 'https://pub.dev/packages/ack', + placement: 'nav', + }, + { + label: 'Concepta', + url: 'https://concepta.dev', + placement: 'menu', + }, + ], +}); diff --git a/docs-site/lib/layout.shared.tsx b/docs-site/lib/layout.shared.tsx new file mode 100644 index 00000000..679a33ae --- /dev/null +++ b/docs-site/lib/layout.shared.tsx @@ -0,0 +1,6 @@ +import { createBaseLayoutOptions } from '@conceptadev/docs-theme'; +import { docsConfig } from '@/docs.config'; + +export function baseOptions() { + return createBaseLayoutOptions(docsConfig); +} diff --git a/docs-site/lib/routes.ts b/docs-site/lib/routes.ts new file mode 100644 index 00000000..d30f1e90 --- /dev/null +++ b/docs-site/lib/routes.ts @@ -0,0 +1,10 @@ +import { addBasePath, createSiteUrl } from '@conceptadev/docs-theme'; +import { docsConfig } from '@/docs.config'; + +/** Only for raw fetches/assets; Next.js Link adds basePath itself. */ +export function withBasePath(path: string): string { + return addBasePath(process.env.NEXT_PUBLIC_DOCS_BASE_PATH ?? '', path); +} +export function absoluteSiteUrl(path: string): string { + return createSiteUrl(docsConfig.site.url, path); +} diff --git a/docs-site/lib/source.ts b/docs-site/lib/source.ts new file mode 100644 index 00000000..95f22709 --- /dev/null +++ b/docs-site/lib/source.ts @@ -0,0 +1,34 @@ +import { PAGE_STATUSES } from '@conceptadev/docs-theme/status'; +import { loader } from 'fumadocs-core/source'; +import { metaSchema, pageSchema } from 'fumadocs-core/source/schema'; +import { defineDocs } from 'fumadocs-mdx/macro'; +import { z } from 'zod'; +import { absoluteSiteUrl } from '@/lib/routes'; + +const docs = defineDocs({ + dir: 'content/docs', + docs: { + schema: pageSchema.extend({ + status: z.enum(PAGE_STATUSES).optional(), + owner: z.string().min(1).optional(), + lastReviewed: z.union([z.date(), z.iso.date().transform((value) => new Date(`${value}T00:00:00Z`))]).optional(), + audience: z.array(z.string()).default([]), + tags: z.array(z.string()).default([]), + }), + postprocess: { includeProcessedMarkdown: true }, + }, + meta: { schema: metaSchema }, +}); +export const source = loader({ baseUrl: '/', source: docs.toFumadocsSource() }); +export function getPageImageUrl(page: (typeof source)['$inferPage']) { + const segments = [...page.slugs, 'image.png']; + return { segments, url: `/${['og', ...segments].join('/')}` }; +} +export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) { + const segments = [...page.slugs, 'content.md']; + return { segments, url: `/${['llms.mdx', ...segments].join('/')}` }; +} +export async function getLLMText(page: (typeof source)['$inferPage']) { + const processed = await page.data.getText('processed'); + return `# ${page.data.title} (${absoluteSiteUrl(page.url)})\n\n${processed}`; +} diff --git a/docs-site/next-env.d.ts b/docs-site/next-env.d.ts new file mode 100644 index 00000000..2f6944fe --- /dev/null +++ b/docs-site/next-env.d.ts @@ -0,0 +1,4 @@ +/// +/// + +// This file is generated by Next.js and should not be edited. diff --git a/docs-site/next.config.mjs b/docs-site/next.config.mjs new file mode 100644 index 00000000..3f2667ec --- /dev/null +++ b/docs-site/next.config.mjs @@ -0,0 +1,23 @@ +import { createMDX } from 'fumadocs-mdx/next'; + +const configured = process.env.DOCS_BASE_PATH ?? ''; +const basePath = configured === '/' ? '' : configured.replace(/\/+$/, ''); +if (basePath && (!/^\/(?!\/)[a-zA-Z0-9/_-]+$/.test(basePath) || basePath.includes('//'))) { + throw new Error('DOCS_BASE_PATH must be a path such as /ack.'); +} +const siteUrl = process.env.NEXT_PUBLIC_SITE_URL; +if (process.env.NODE_ENV === 'production' && !siteUrl) throw new Error('Set NEXT_PUBLIC_SITE_URL for production builds.'); +if (siteUrl && new URL(siteUrl).pathname.replace(/\/+$/, '') !== basePath) { + throw new Error('NEXT_PUBLIC_SITE_URL pathname must match DOCS_BASE_PATH.'); +} +/** @type {import('next').NextConfig} */ +const config = { + output: 'export', + trailingSlash: true, + reactStrictMode: true, + basePath: basePath || undefined, + images: { unoptimized: true }, + transpilePackages: ['@conceptadev/docs-theme'], + env: { NEXT_PUBLIC_DOCS_BASE_PATH: basePath }, +}; +export default createMDX()(config); diff --git a/docs-site/package.json b/docs-site/package.json new file mode 100644 index 00000000..44a6d4d6 --- /dev/null +++ b/docs-site/package.json @@ -0,0 +1,40 @@ +{ + "name": "@conceptadev/ack-docs", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "docs:sync": "node scripts/sync-docs.mjs", + "build": "pnpm docs:sync && next build", + "dev": "node scripts/dev.mjs", + "preview": "python3 scripts/preview.py", + "test": "node --test tests/*.test.mjs", + "test:browser": "playwright test", + "typecheck": "pnpm docs:sync && next typegen && tsc --noEmit", + "check": "pnpm test && pnpm typecheck && pnpm build && python3 scripts/verify-export.py" + }, + "dependencies": { + "@base-ui/react": "1.7.0", + "@conceptadev/docs-theme": "workspace:*", + "fumadocs-core": "16.15.8", + "fumadocs-mdx": "15.4.0", + "fumadocs-ui": "npm:@fumadocs/base-ui@16.15.8", + "next": "16.3.4", + "react": "19.2.8", + "react-dom": "19.2.8", + "zod": "4.4.3" + }, + "devDependencies": { + "@playwright/test": "1.63.0", + "@tailwindcss/postcss": "4.3.3", + "@types/mdx": "2.0.14", + "@types/node": "26.3.0", + "@types/react": "19.2.18", + "@types/react-dom": "19.2.5", + "postcss": "8.5.26", + "tailwindcss": "4.3.3", + "typescript": "6.0.3" + }, + "engines": { "node": ">=24.14.0" }, + "packageManager": "pnpm@11.5.3" +} diff --git a/docs-site/packages/docs-theme/SOURCE.md b/docs-site/packages/docs-theme/SOURCE.md new file mode 100644 index 00000000..aee86f28 --- /dev/null +++ b/docs-site/packages/docs-theme/SOURCE.md @@ -0,0 +1,17 @@ +# Source + +This is the only local `@conceptadev/docs-theme` package in Ack. +It tracks the public API and source corrections in Concepta docs-theme PR #2, +source revision `117abf35b821603fa63869db2d45f365284ef1ee`. + +The package manifest uses source exports because Next.js transpiles this local +workspace package. Its explicit Base UI development dependency prevents a +second Radix-based UI implementation from being installed for the peer. +The separate `status` export is safe for Node-based MDX configuration evaluation; +it does not load React components or extensionless UI modules. + +The shared package remains private and unpublished. Once a reviewed release is +available, replace `workspace:*` with its version, regenerate the lockfile, +remove this directory, and rerun the root/subpath build and browser checks. +Do not edit this snapshot independently without bringing the shared source +change back to the theme repository. diff --git a/docs-site/packages/docs-theme/package.json b/docs-site/packages/docs-theme/package.json new file mode 100644 index 00000000..de8dec8f --- /dev/null +++ b/docs-site/packages/docs-theme/package.json @@ -0,0 +1,24 @@ +{ + "name": "@conceptadev/docs-theme", + "version": "0.1.0-local.1", + "private": true, + "type": "module", + "description": "Reviewed local snapshot of the Concepta Fumadocs theme.", + "exports": { + ".": "./src/index.ts", + "./mdx": "./src/mdx.tsx", + "./status": "./src/status.ts", + "./theme.css": "./src/theme.css", + "./package.json": "./package.json" + }, + "sideEffects": ["./src/theme.css"], + "peerDependencies": { + "@types/mdx": ">=2", + "fumadocs-core": ">=16.15.8 <17", + "fumadocs-ui": ">=16.15.8 <17", + "next": ">=16.3.4 <17", + "react": ">=19.2.0 <20", + "react-dom": ">=19.2.0 <20" + }, + "devDependencies": { "fumadocs-ui": "npm:@fumadocs/base-ui@16.15.8" } +} diff --git a/docs-site/packages/docs-theme/src/components/concept-summary.tsx b/docs-site/packages/docs-theme/src/components/concept-summary.tsx new file mode 100644 index 00000000..9b51429c --- /dev/null +++ b/docs-site/packages/docs-theme/src/components/concept-summary.tsx @@ -0,0 +1,30 @@ +import type { ComponentProps, ReactNode } from 'react'; + +export interface ConceptSummaryProps + extends Omit, 'title'> { + title?: ReactNode; +} + +export function ConceptSummary({ + title = 'Concept summary', + children, + className, + ...props +}: ConceptSummaryProps) { + return ( +
+

+ {title} +

+
{children}
+
+ ); +} diff --git a/docs-site/packages/docs-theme/src/components/project-links.tsx b/docs-site/packages/docs-theme/src/components/project-links.tsx new file mode 100644 index 00000000..32b7484b --- /dev/null +++ b/docs-site/packages/docs-theme/src/components/project-links.tsx @@ -0,0 +1,26 @@ +import Link from 'fumadocs-core/link'; +import type { ComponentProps, ReactNode } from 'react'; +import type { DocsProject } from '../types'; + +export interface ProjectLinksProps extends Omit, 'title'> { + projects: DocsProject[]; + title?: ReactNode; +} +export function ProjectLinks({ projects, title = 'Related projects', className, ...props }: ProjectLinksProps) { + if (projects.length === 0) return null; + return ( +
+

{title}

+
    + {projects.map((project) => ( +
  • + + {project.name} + {project.description ? {project.description} : null} + +
  • + ))} +
+
+ ); +} diff --git a/docs-site/packages/docs-theme/src/components/status.tsx b/docs-site/packages/docs-theme/src/components/status.tsx new file mode 100644 index 00000000..3623b76e --- /dev/null +++ b/docs-site/packages/docs-theme/src/components/status.tsx @@ -0,0 +1,24 @@ +import type { ComponentProps } from 'react'; +import type { PageStatus } from '../types'; + +export interface StatusProps extends Omit, 'children'> { + status: PageStatus; + label?: string; +} + +export function Status({ + status, + label = status, + className, + ...props +}: StatusProps) { + return ( + + {label} + + ); +} diff --git a/docs-site/packages/docs-theme/src/config.ts b/docs-site/packages/docs-theme/src/config.ts new file mode 100644 index 00000000..22260452 --- /dev/null +++ b/docs-site/packages/docs-theme/src/config.ts @@ -0,0 +1,53 @@ +import type { DocsConfig, DocsProject } from './types'; +import { assertAppPath, createSiteUrl, normalizeBasePath } from './urls'; + +const PROJECT_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/; + +function assertHttpUrl(value: string, name: string): void { + const url = new URL(value); + if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password) { + throw new Error(`${name} must be an HTTP(S) URL without credentials.`); + } +} +function assertLink(value: string, name: string): void { + if (value.startsWith('/')) assertAppPath(value); + else assertHttpUrl(value, name); +} +function assertProject(project: DocsProject, name: string): void { + if (!PROJECT_ID_PATTERN.test(project.id)) throw new Error(`${name}.id must contain lowercase letters, numbers, and hyphens.`); + if (!project.name.trim()) throw new Error(`${name}.name must not be empty.`); + assertLink(project.docsUrl, `${name}.docsUrl`); + if (project.repository) { + createSiteUrl(project.repository.url); + if (project.repository.branch !== undefined && !project.repository.branch.trim()) throw new Error(`${name}.repository.branch must not be empty.`); + if (project.repository.contentPath) assertAppPath(project.repository.contentPath); + } +} + +/** Validate the small data-only layer; Fumadocs still owns routing and MDX. */ +export function defineDocsConfig(config: T): T { + if (!config.organization.name.trim()) throw new Error('organization.name must not be empty.'); + if (!config.site.title.trim() || !config.site.description.trim()) throw new Error('site.title and site.description must not be empty.'); + createSiteUrl(config.site.url); + if (config.organization.homeUrl) assertHttpUrl(config.organization.homeUrl, 'organization.homeUrl'); + if (config.site.docsPath !== undefined) normalizeBasePath(config.site.docsPath); + assertProject(config.project, 'project'); + const seen = new Set(); + for (const [index, project] of (config.projects ?? []).entries()) { + assertProject(project, `projects[${index}]`); + if (seen.has(project.id)) throw new Error(`projects contains the duplicate id "${project.id}".`); + seen.add(project.id); + } + for (const [index, link] of (config.socialLinks ?? []).entries()) { + if (!link.label.trim()) throw new Error(`socialLinks[${index}].label must not be empty.`); + assertLink(link.url, `socialLinks[${index}].url`); + } + return config; +} +export function getDocsPath(config: DocsConfig): `/${string}` { + return config.site.docsPath ?? '/docs'; +} +export function getProjects(config: DocsConfig): DocsProject[] { + const projects = config.projects ?? []; + return projects.some((project) => project.id === config.project.id) ? projects : [config.project, ...projects]; +} diff --git a/docs-site/packages/docs-theme/src/index.ts b/docs-site/packages/docs-theme/src/index.ts new file mode 100644 index 00000000..ea132d1a --- /dev/null +++ b/docs-site/packages/docs-theme/src/index.ts @@ -0,0 +1,14 @@ +export { ConceptSummary } from './components/concept-summary'; +export type { ConceptSummaryProps } from './components/concept-summary'; +export { ProjectLinks } from './components/project-links'; +export type { ProjectLinksProps } from './components/project-links'; +export { Status } from './components/status'; +export type { StatusProps } from './components/status'; +export { defineDocsConfig, getDocsPath, getProjects } from './config'; +export { createBaseLayoutOptions } from './layout'; +export { createPageMetadata, createSiteMetadata } from './metadata'; +export type { PageMetadataInput } from './metadata'; +export { createEditUrl, createRepositoryUrl, createSourceUrl } from './repository'; +export { PAGE_STATUSES } from './status'; +export { addBasePath, createSiteUrl, normalizeBasePath } from './urls'; +export type { DocsConfig, DocsProject, DocsRepository, DocsSocialLink, LinkPlacement, PageStatus } from './types'; diff --git a/docs-site/packages/docs-theme/src/layout.tsx b/docs-site/packages/docs-theme/src/layout.tsx new file mode 100644 index 00000000..aec68a1b --- /dev/null +++ b/docs-site/packages/docs-theme/src/layout.tsx @@ -0,0 +1,66 @@ +import type { + BaseLayoutProps, + LinkItemType, + MainItemType, +} from 'fumadocs-ui/layouts/shared'; +import { getDocsPath, getProjects } from './config'; +import { createRepositoryUrl } from './repository'; +import type { DocsConfig } from './types'; + +function isExternalUrl(url: string, siteUrl: string): boolean { + if (url.startsWith('/')) return false; + + try { + return new URL(url).origin !== new URL(siteUrl).origin; + } catch { + return true; + } +} + +function createProjectMenu(config: DocsConfig): LinkItemType | undefined { + const projects = getProjects(config); + if (projects.length < 2) return undefined; + + const items: MainItemType[] = projects.map((project) => ({ + type: 'main', + text: project.name, + description: project.description, + url: project.docsUrl, + active: 'nested-url', + external: isExternalUrl(project.docsUrl, config.site.url), + })); + + return { + type: 'menu', + text: 'Projects', + items, + }; +} + +export function createBaseLayoutOptions( + config: DocsConfig, +): BaseLayoutProps { + const links: LinkItemType[] = []; + const projectMenu = createProjectMenu(config); + + if (projectMenu) links.push(projectMenu); + + for (const link of config.socialLinks ?? []) { + links.push({ + type: 'main', + text: link.label, + url: link.url, + on: link.placement ?? 'menu', + external: isExternalUrl(link.url, config.site.url), + }); + } + + return { + nav: { + title: config.project.name, + url: getDocsPath(config), + }, + githubUrl: createRepositoryUrl(config), + links, + }; +} diff --git a/docs-site/packages/docs-theme/src/mdx.tsx b/docs-site/packages/docs-theme/src/mdx.tsx new file mode 100644 index 00000000..4a93d084 --- /dev/null +++ b/docs-site/packages/docs-theme/src/mdx.tsx @@ -0,0 +1,19 @@ +import { Accordion, Accordions } from 'fumadocs-ui/components/accordion'; +import { File, Files, Folder } from 'fumadocs-ui/components/files'; +import { Step, Steps } from 'fumadocs-ui/components/steps'; +import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; +import defaultMdxComponents from 'fumadocs-ui/mdx'; +import type { MDXComponents } from 'mdx/types'; +import { ConceptSummary } from './components/concept-summary'; +import { ProjectLinks } from './components/project-links'; +import { Status } from './components/status'; + +export function getConceptaMDXComponents(components?: MDXComponents) { + return { + ...defaultMdxComponents, + Accordion, Accordions, File, Files, Folder, Step, Steps, Tab, Tabs, + ConceptSummary, ProjectLinks, Status, + ...components, + } satisfies MDXComponents; +} +export const useMDXComponents = getConceptaMDXComponents; diff --git a/docs-site/packages/docs-theme/src/metadata.ts b/docs-site/packages/docs-theme/src/metadata.ts new file mode 100644 index 00000000..02b3d944 --- /dev/null +++ b/docs-site/packages/docs-theme/src/metadata.ts @@ -0,0 +1,35 @@ +import type { Metadata } from 'next'; +import type { DocsConfig } from './types'; +import { createSiteUrl } from './urls'; + +export interface PageMetadataInput { + title: string; + description?: string; + /** Application-relative page URL, including docsPath once. */ + path: string; + imageUrl?: string; +} +export function createSiteMetadata(config: DocsConfig): Metadata { + const siteUrl = new URL(createSiteUrl(config.site.url)); + return { + metadataBase: siteUrl, + title: { default: config.site.title, template: `%s | ${config.site.title}` }, + description: config.site.description, + applicationName: config.site.title, + alternates: { canonical: siteUrl }, + openGraph: { type: 'website', siteName: config.site.title, title: config.site.title, description: config.site.description, url: siteUrl }, + twitter: { card: 'summary_large_image', title: config.site.title, description: config.site.description }, + }; +} +export function createPageMetadata(config: DocsConfig, input: PageMetadataInput): Metadata { + const pageUrl = createSiteUrl(config.site.url, input.path); + const images = input.imageUrl ? [createSiteUrl(config.site.url, input.imageUrl)] : undefined; + const description = input.description ?? config.site.description; + return { + title: input.title, + description, + alternates: { canonical: pageUrl }, + openGraph: { type: 'article', siteName: config.site.title, title: input.title, description, url: pageUrl, images }, + twitter: { card: 'summary_large_image', title: input.title, description, images }, + }; +} diff --git a/docs-site/packages/docs-theme/src/repository.ts b/docs-site/packages/docs-theme/src/repository.ts new file mode 100644 index 00000000..98129395 --- /dev/null +++ b/docs-site/packages/docs-theme/src/repository.ts @@ -0,0 +1,25 @@ +import type { DocsConfig } from './types'; + +function encodePath(...parts: Array): string { + return parts.filter((part): part is string => Boolean(part)) + .flatMap((part) => part.split('/')).filter(Boolean) + .map((part) => { + if (part === '.' || part === '..' || /[\\\u0000-\u001f]/.test(part)) throw new Error('Repository paths must not contain traversal.'); + return encodeURIComponent(part); + }).join('/'); +} +export function createRepositoryUrl(config: DocsConfig): string | undefined { + return config.project.repository?.url.replace(/\/+$/, ''); +} +function fileUrl(config: DocsConfig, pagePath: string, action: 'blob' | 'edit'): string | undefined { + const repository = config.project.repository; + if (!repository) return undefined; + const branch = encodeURIComponent(repository.branch ?? 'main'); + return `${createRepositoryUrl(config)}/${action}/${branch}/${encodePath(repository.contentPath, pagePath)}`; +} +export function createSourceUrl(config: DocsConfig, pagePath: string): string | undefined { + return fileUrl(config, pagePath, 'blob'); +} +export function createEditUrl(config: DocsConfig, pagePath: string): string | undefined { + return fileUrl(config, pagePath, 'edit'); +} diff --git a/docs-site/packages/docs-theme/src/status.ts b/docs-site/packages/docs-theme/src/status.ts new file mode 100644 index 00000000..265c6527 --- /dev/null +++ b/docs-site/packages/docs-theme/src/status.ts @@ -0,0 +1,2 @@ +export const PAGE_STATUSES = ['beta', 'deprecated', 'draft', 'experimental', 'stable'] as const; +export type PageStatus = (typeof PAGE_STATUSES)[number]; diff --git a/docs-site/packages/docs-theme/src/theme.css b/docs-site/packages/docs-theme/src/theme.css new file mode 100644 index 00000000..452c398d --- /dev/null +++ b/docs-site/packages/docs-theme/src/theme.css @@ -0,0 +1,46 @@ +/* Tailwind resolves these paths relative to this shipped stylesheet. */ +@source '../dist'; +@source './components'; + +:root { + --fd-layout-width: 1440px; + --color-fd-primary: hsl(221 83% 53%); + --color-fd-primary-foreground: hsl(0 0% 100%); +} +.dark { + --color-fd-primary: hsl(217 91% 60%); + --color-fd-primary-foreground: hsl(222 47% 11%); +} +@layer components { + .concepta-status { + display: inline-flex; + align-items: center; + border: 1px solid var(--color-fd-border); + border-radius: 9999px; + background: var(--color-fd-muted); + padding: 0.125rem 0.5rem; + font-size: 0.75rem; + font-weight: 600; + line-height: 1.25rem; + text-transform: capitalize; + color: var(--color-fd-muted-foreground); + } + .concepta-status[data-status='stable'] { + border-color: color-mix(in oklab, #16a34a 35%, transparent); + background: color-mix(in oklab, #16a34a 12%, transparent); + color: #15803d; + } + .dark .concepta-status[data-status='stable'] { color: #86efac; } + .concepta-status[data-status='beta'], .concepta-status[data-status='experimental'] { + border-color: color-mix(in oklab, #d97706 35%, transparent); + background: color-mix(in oklab, #d97706 12%, transparent); + color: #b45309; + } + .dark .concepta-status[data-status='beta'], .dark .concepta-status[data-status='experimental'] { color: #fcd34d; } + .concepta-status[data-status='deprecated'] { + border-color: color-mix(in oklab, #dc2626 35%, transparent); + background: color-mix(in oklab, #dc2626 12%, transparent); + color: #b91c1c; + } + .dark .concepta-status[data-status='deprecated'] { color: #fca5a5; } +} diff --git a/docs-site/packages/docs-theme/src/types.ts b/docs-site/packages/docs-theme/src/types.ts new file mode 100644 index 00000000..eb601e78 --- /dev/null +++ b/docs-site/packages/docs-theme/src/types.ts @@ -0,0 +1,36 @@ +export type { PageStatus } from './status'; +export type LinkPlacement = 'all' | 'menu' | 'nav'; + +export interface DocsRepository { + /** Repository root URL, including GitHub Enterprise URLs. */ + url: string; + branch?: string; + /** Repository-relative path to the documentation content. */ + contentPath?: string; +} +export interface DocsProject { + id: string; + name: string; + description?: string; + docsUrl: string; + repository?: DocsRepository; +} +export interface DocsSocialLink { + label: string; + url: string; + placement?: LinkPlacement; +} +export interface DocsConfig { + organization: { name: string; homeUrl?: string }; + site: { + title: string; + description: string; + /** Public application URL, including its deployment path, for example /ack. */ + url: string; + /** Documentation route inside the application, not the deployment path. */ + docsPath?: `/${string}`; + }; + project: DocsProject; + projects?: DocsProject[]; + socialLinks?: DocsSocialLink[]; +} diff --git a/docs-site/packages/docs-theme/src/urls.ts b/docs-site/packages/docs-theme/src/urls.ts new file mode 100644 index 00000000..49bbe65f --- /dev/null +++ b/docs-site/packages/docs-theme/src/urls.ts @@ -0,0 +1,46 @@ +const BAD_PATH = /[\\\u0000-\u0020]/; + +export function assertAppPath(value: string): void { + if (BAD_PATH.test(value) || value.startsWith('//')) { + throw new Error('Use an application-relative path, not a protocol-relative URL.'); + } + const pathname = value.split(/[?#]/, 1)[0]; + for (const part of pathname.split('/')) { + const decoded = decodeURIComponent(part); + if (decoded === '.' || decoded === '..' || /[\\/\u0000-\u001f]/.test(decoded)) { + throw new Error('Path must not contain traversal or encoded separators.'); + } + } +} + +export function normalizeBasePath(value = ''): string { + if (value === '' || value === '/') return ''; + assertAppPath(value); + if (!value.startsWith('/') || /[?#]/.test(value)) { + throw new Error('basePath must start with / and contain no query or fragment.'); + } + return value.replace(/\/+$/, ''); +} + +/** Prefix a raw fetch/asset URL. Do not use on Next.js Link hrefs. */ +export function addBasePath(basePath: string, path: string): string { + const base = normalizeBasePath(basePath); + assertAppPath(path); + return `${base}${path.startsWith('/') ? path : `/${path}`}`; +} + +/** Resolve an application-relative path while retaining the site's deployment path. */ +export function createSiteUrl(siteUrl: string, path = '/'): string { + const base = new URL(siteUrl); + if (!['http:', 'https:'].includes(base.protocol) || base.username || base.password || base.search || base.hash) { + throw new Error('site.url must be an HTTP(S) URL without credentials, query, or fragment.'); + } + if (/^https?:\/\//i.test(path)) { + const absolute = new URL(path); + if (absolute.username || absolute.password) throw new Error('URL must not contain credentials.'); + return absolute.toString(); + } + assertAppPath(path); + base.pathname = `${base.pathname.replace(/\/+$/, '')}/`; + return new URL(path.replace(/^\/+/, ''), base).toString(); +} diff --git a/docs-site/playwright.config.ts b/docs-site/playwright.config.ts new file mode 100644 index 00000000..a466369c --- /dev/null +++ b/docs-site/playwright.config.ts @@ -0,0 +1,22 @@ +import { defineConfig, devices } from '@playwright/test'; +const basePath = (process.env.DOCS_BASE_PATH ?? '').replace(/\/+$/, ''); +export default defineConfig({ + testDir: './tests/browser', + timeout: 30000, + fullyParallel: false, + forbidOnly: Boolean(process.env.CI), + retries: 0, + workers: 1, + reporter: 'list', + use: { baseURL: `http://127.0.0.1:4173${basePath}/`, trace: 'retain-on-failure' }, + projects: [ + { name: 'desktop', use: { ...devices['Desktop Chrome'] } }, + { name: 'mobile', use: { ...devices['iPhone 13'], defaultBrowserType: 'chromium' } }, + ], + webServer: { + command: 'python3 scripts/preview.py', + url: `http://127.0.0.1:4173${basePath}/`, + reuseExistingServer: false, + timeout: 30000, + }, +}); diff --git a/docs-site/pnpm-lock.yaml b/docs-site/pnpm-lock.yaml new file mode 100644 index 00000000..308d8775 --- /dev/null +++ b/docs-site/pnpm-lock.yaml @@ -0,0 +1,3591 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: false + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@base-ui/react': + specifier: 1.7.0 + version: 1.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@conceptadev/docs-theme': + specifier: workspace:* + version: link:packages/docs-theme + fumadocs-core: + specifier: 16.15.8 + version: 16.15.8(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.18)(lucide-react@1.39.0(react@19.2.8))(next@16.3.4(@playwright/test@1.63.0)(@types/node@26.3.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3) + fumadocs-mdx: + specifier: 15.4.0 + version: 15.4.0(@types/mdast@4.0.4)(@types/mdx@2.0.14)(@types/react@19.2.18)(fumadocs-core@16.15.8(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.18)(lucide-react@1.39.0(react@19.2.8))(next@16.3.4(@playwright/test@1.63.0)(@types/node@26.3.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.4(@playwright/test@1.63.0)(@types/node@26.3.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8) + fumadocs-ui: + specifier: npm:@fumadocs/base-ui@16.15.8 + version: '@fumadocs/base-ui@16.15.8(@types/mdx@2.0.14)(@types/react@19.2.18)(fumadocs-core@16.15.8(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.18)(lucide-react@1.39.0(react@19.2.8))(next@16.3.4(@playwright/test@1.63.0)(@types/node@26.3.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.4(@playwright/test@1.63.0)(@types/node@26.3.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.3.3)' + next: + specifier: 16.3.4 + version: 16.3.4(@playwright/test@1.63.0)(@types/node@26.3.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + react: + specifier: 19.2.8 + version: 19.2.8 + react-dom: + specifier: 19.2.8 + version: 19.2.8(react@19.2.8) + zod: + specifier: 4.4.3 + version: 4.4.3 + devDependencies: + '@playwright/test': + specifier: 1.63.0 + version: 1.63.0 + '@tailwindcss/postcss': + specifier: 4.3.3 + version: 4.3.3 + '@types/mdx': + specifier: 2.0.14 + version: 2.0.14 + '@types/node': + specifier: 26.3.0 + version: 26.3.0 + '@types/react': + specifier: 19.2.18 + version: 19.2.18 + '@types/react-dom': + specifier: 19.2.5 + version: 19.2.5(@types/react@19.2.18) + postcss: + specifier: 8.5.26 + version: 8.5.26 + tailwindcss: + specifier: 4.3.3 + version: 4.3.3 + typescript: + specifier: 6.0.3 + version: 6.0.3 + + packages/docs-theme: + devDependencies: + fumadocs-ui: + specifier: npm:@fumadocs/base-ui@16.15.8 + version: '@fumadocs/base-ui@16.15.8(@types/mdx@2.0.14)(@types/react@19.2.18)(fumadocs-core@16.15.8(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.18)(lucide-react@1.39.0(react@19.2.8))(next@16.3.4(@playwright/test@1.63.0)(@types/node@26.3.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.4(@playwright/test@1.63.0)(@types/node@26.3.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.3.3)' + +packages: + + '@alloc/quick-lru@5.3.0': + resolution: {integrity: sha512-U4+70Pc5ZS9osnCBCE5Jha/ciHM+Yp+CNMNC/7HvYbNRk1Ldd+f7qO65W5qfhu/TCv+/ozljlXXe9Nj8419DMA==} + engines: {node: '>=10'} + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + + '@base-ui/react@1.7.0': + resolution: {integrity: sha512-j+8QjX44C32jrXD/qyEAGpFr70FRpGL2CY61mQd9nBPWN737CK0xxD1ceJ055rW4RtdvFDT1e7otzdlfxvsYug==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@date-fns/tz': ^1.2.0 + '@types/react': ^17 || ^18 || ^19 + date-fns: ^4.0.0 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + '@date-fns/tz': + optional: true + '@types/react': + optional: true + date-fns: + optional: true + + '@base-ui/utils@0.3.2': + resolution: {integrity: sha512-oWy1aq/I2GmYjpl4PhEAhzflF8VPGKgZeq0xAWTbfD5KBWyxcN0ZP2+WHSUm/5Z6lVMBDLReLcoXwSYoRc/zNQ==} + peerDependencies: + '@types/react': ^17 || ^18 || ^19 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + '@types/react': + optional: true + + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + + '@esbuild/aix-ppc64@0.28.2': + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.28.2': + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.28.2': + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.28.2': + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.28.2': + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.2': + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.28.2': + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.2': + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.28.2': + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.28.2': + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.28.2': + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.28.2': + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.28.2': + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.28.2': + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.2': + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.28.2': + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.28.2': + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.28.2': + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.2': + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.28.2': + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.2': + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.28.2': + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.28.2': + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.28.2': + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.28.2': + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.28.2': + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} + + '@floating-ui/react-dom@2.1.9': + resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + + '@fuma-translate/react@1.0.2': + resolution: {integrity: sha512-uOiOtBx3nRXR8Nu1GzBf1tApgF1FErDBTHxRIAQeyQdyOoZbrNRN6H4kDCWObY4qyGeGbHydG0DHzgeUgFDMIw==} + peerDependencies: + '@types/react': '*' + react: ^19.2.0 + react-dom: ^19.2.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@fumadocs/base-ui@16.15.8': + resolution: {integrity: sha512-zbtEwDIDnbGqCu3ZbyaAAQG8Pxvh/cMfFzH8mtuKjn4QQEPw3tggelElzftOxD8vKy9QdDGHzvmUfVX71U+/LQ==} + peerDependencies: + '@types/mdx': '*' + '@types/react': '*' + fumadocs-core: 16.15.8 + next: 16.x.x + react: ^19.2.0 + react-dom: ^19.2.0 + takumi-js: '*' + peerDependenciesMeta: + '@types/mdx': + optional: true + '@types/react': + optional: true + next: + optional: true + takumi-js: + optional: true + + '@fumadocs/tailwind@0.1.1': + resolution: {integrity: sha512-BnPe52UxSaG8yKlHMKBxXw8h6GpK5qO55ci6+Qd5JnquTvIw6SpfbC1P+qAi82PuPWv1KZAWY8bxRk4+x9ctXw==} + peerDependencies: + tailwindcss: ^4.0.0 + peerDependenciesMeta: + tailwindcss: + optional: true + + '@fumari/image-size@0.1.0': + resolution: {integrity: sha512-x2o9u6P8uKUK15B8XgEoRhR3PgLoLSbQKK6FUCd14JzumEw+e8FXZPelij/dZ4VQVMp4r61VD3DcvSo3aEhLAA==} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.35.4': + resolution: {integrity: sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.35.4': + resolution: {integrity: sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-freebsd-wasm32@0.35.4': + resolution: {integrity: sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==} + engines: {node: '>=20.9.0'} + os: [freebsd] + + '@img/sharp-libvips-darwin-arm64@1.3.3': + resolution: {integrity: sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.3.3': + resolution: {integrity: sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.3.3': + resolution: {integrity: sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.3.3': + resolution: {integrity: sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-ppc64@1.3.3': + resolution: {integrity: sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.3.3': + resolution: {integrity: sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.3.3': + resolution: {integrity: sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.3.3': + resolution: {integrity: sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.3.3': + resolution: {integrity: sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.3.3': + resolution: {integrity: sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.35.4': + resolution: {integrity: sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.35.4': + resolution: {integrity: sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==} + engines: {node: '>=20.9.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-ppc64@0.35.4': + resolution: {integrity: sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==} + engines: {node: '>=20.9.0'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.35.4': + resolution: {integrity: sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==} + engines: {node: '>=20.9.0'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.35.4': + resolution: {integrity: sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==} + engines: {node: '>=20.9.0'} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.35.4': + resolution: {integrity: sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.35.4': + resolution: {integrity: sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.35.4': + resolution: {integrity: sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.35.4': + resolution: {integrity: sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.4': + resolution: {integrity: sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==} + engines: {node: '>=20.9.0'} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.35.4': + resolution: {integrity: sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.35.4': + resolution: {integrity: sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==} + engines: {node: ^20.9.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.35.4': + resolution: {integrity: sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@mdx-js/mdx@3.1.1': + resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} + + '@next/env@16.3.4': + resolution: {integrity: sha512-cjWZnUUa6jZq2kFaNe/ZyJdZonOZ/QoN0Zka2nz/FLOrfx14pQuM9c5RaSVkWMqgdt4ksgPAMWPyHSs/CyV48Q==} + + '@next/swc-darwin-arm64@16.3.4': + resolution: {integrity: sha512-iBr3I5LZNk5/bgl5//iTgD2tcym14MX0Xo7fD//u9dYAEgGzza1y9oywluPtf74YnOswVdH1908aK9xVz7zQTw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@16.3.4': + resolution: {integrity: sha512-2dpiSyl2Jw/NrBPaU2MAKGSa+2MR82pJIn4Sm5Rjr+gxAeuh0z158Su3Z2O8zn7UNNq+ej4bToed6RcRN/Lydg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@16.3.4': + resolution: {integrity: sha512-+t+U8HZT+fApePCS5h89CSH3datz29MkzyfCn+6fpsZBG/oiEOhINcb9rtkv6sdpToLGFn2e6146NzaKCXkqrA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@next/swc-linux-arm64-musl@16.3.4': + resolution: {integrity: sha512-mx03GNs1ocQA5JQ4FxDMmIsNkdrZh8cuezKCrId28e5/gIPU/l7Kcy2+vmCCzdjnnmXJy+iOAu+7K0QppO6Urg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@next/swc-linux-x64-gnu@16.3.4': + resolution: {integrity: sha512-YIhGY6fSMfha52bnVxnzc9zaVBzJg+cqQTOD8tXIBSx4fuv0pVMxQTE0PaS59YhnMOiYiG09IMwxJAf/CFm/Dw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@next/swc-linux-x64-musl@16.3.4': + resolution: {integrity: sha512-+eaaX6axpDb0yF1GCpiERe6njplvdC+nks/fKfcHu3XPGRrald8P3/X7yv7QLdjA51knnxwl9pxdIJsg+w1L+Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@next/swc-win32-arm64-msvc@16.3.4': + resolution: {integrity: sha512-0jcXW7Xs/uzICrmgV3MhDYDeRy++1CqnpDIerlPIqYO4bhzB4WNbX/aRnQclustsAyTkFKB0z6rbcjmNg5tR8A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@16.3.4': + resolution: {integrity: sha512-vvBzwu1pYQCp92maZCFCIw/XgOTMR5tur9GjakwIo2cmwRTMKajRZZDS9+e4KsUZWKu1E007WUeAFXRRjZeuzw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@playwright/test@1.63.0': + resolution: {integrity: sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ==} + engines: {node: '>=20'} + hasBin: true + + '@shikijs/core@4.4.3': + resolution: {integrity: sha512-QCR4q2ZO/ILJEuwiBMel4wdcTDb1JGwfjKTxPDF6x8ixOaluPrVqIn06C99AcRPhmYlBR56d/Fb+GN58GzExpg==} + engines: {node: '>=20'} + + '@shikijs/engine-javascript@4.4.3': + resolution: {integrity: sha512-FbOjFJp9VLdo1Wevs10BBtVxiTWwNLqZh5Gkhjgda/ioL15YOgeSl9n+6XMa3qRlPQzfhFNe641SrynFHYG0nQ==} + engines: {node: '>=20'} + + '@shikijs/engine-oniguruma@4.4.3': + resolution: {integrity: sha512-EcOQkxdxGQrc1Row/cC2c96/v1dbZqGnEVu1qTuT/MJmp6+cXCvQussowVmCv5Tqr3KuY3c7IbM6HTW3LJ1k9w==} + engines: {node: '>=20'} + + '@shikijs/langs@4.4.3': + resolution: {integrity: sha512-ePic0yfAJGOF83D5wBHK/00EjK65oahBYxFk5epgq33WRv7X9UuxLEV8PtR0szC0z8dl7INIpIodB99JRFlR+A==} + engines: {node: '>=20'} + + '@shikijs/primitive@4.4.3': + resolution: {integrity: sha512-m0wBeLDQDeIxRdUmrCPdQqfuUamDwRL5isCfYbguKD6NiaKpVbsv+3J81DyIKgNW5h4WAIIr8T4EkgQrBBxvaQ==} + engines: {node: '>=20'} + + '@shikijs/themes@4.4.3': + resolution: {integrity: sha512-w8UHjeUnIR965KMWJHUPXOc2mNJUnK3vpVLYLvw5IYU2mnTTJ89E24OrJDBNiJDQ0qzb0tc4l7mrIXx5cFeIyw==} + engines: {node: '>=20'} + + '@shikijs/types@4.4.3': + resolution: {integrity: sha512-UEJxmRR++MAGR6hugn0vgVS2W/6lWAts84FFSrnlH9sP0LNol7E5+NQ792pH8liWUhyMyjhTgSUH3k7iD7tc5g==} + engines: {node: '>=20'} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@swc/helpers@0.5.23': + resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} + + '@tailwindcss/node@4.3.3': + resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==} + + '@tailwindcss/oxide-android-arm64@4.3.3': + resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.3': + resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.3': + resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.3': + resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + resolution: {integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + resolution: {integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.3': + resolution: {integrity: sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==} + engines: {node: '>= 20'} + + '@tailwindcss/postcss@4.3.3': + resolution: {integrity: sha512-JTSZZGQi1AyKirbLN3azmjVzef92tcX7h+iSqPdaeStyFpGpDlKvvpxeOE8njhbUanbRwr3z8DyzhICWnMtQeg==} + + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/hast@3.0.5': + resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mdx@2.0.14': + resolution: {integrity: sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@26.3.0': + resolution: {integrity: sha512-L3fgrnchriRC2ExBflb8j4uZZURHZfQsmQeyVzhjcHW4kkwVyo8/0h1B2MVzMTrYUJYu6G7EWs14hW/L9putqw==} + + '@types/react-dom@19.2.5': + resolution: {integrity: sha512-fMPwH9v7r/pp43yUd2/Mbiex5KouJwwR3dzHkhLREUC6764VyDsqxhAxv6OFEYR1RhjOyD1naqba8ECDBe7ZQg==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.18': + resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@ungap/structured-clone@1.4.0': + resolution: {integrity: sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==} + + '@yuku-analyzer/binding-android-arm64@0.9.4': + resolution: {integrity: sha512-ViV7rntyKeSeDQL29jKRjHRdJNFQLfpNGbovAvbABeZZyrAejhzxc6nRDQmMP0bVhMntmS5F/63RmI3h54u1Hg==} + cpu: [arm64] + os: [android] + + '@yuku-analyzer/binding-darwin-arm64@0.9.4': + resolution: {integrity: sha512-iXKB2BgcrIP6ujRYYp2ON+N/ovgJV43X5heK1ugWy4p3Subxr84NvDw4etW2OAX0rGinYps49xpSsIEIm1RlhQ==} + cpu: [arm64] + os: [darwin] + + '@yuku-analyzer/binding-darwin-x64@0.9.4': + resolution: {integrity: sha512-wGd5eFRAuJilscl6GIrWNnSSXi6yggUFaKHYfuUivfa3OWg+6UfoZem/tJWIVWnhVedcKrbgRxOl8e0xel3aew==} + cpu: [x64] + os: [darwin] + + '@yuku-analyzer/binding-freebsd-x64@0.9.4': + resolution: {integrity: sha512-0IuD8KhUeaQckd7PiPTzaTf1XArK1UNf9ZrWrrOhtJGEnv6e0tB83y63RY7NO0xYH9gQbxDpyy64lDOwETj+Tw==} + cpu: [x64] + os: [freebsd] + + '@yuku-analyzer/binding-linux-arm-gnu@0.9.4': + resolution: {integrity: sha512-RnKc+7k8+Ku1SxG4Zf4r84NcMvTT6Mc/lza7gFzim565+fj6IHKKJkyL/dZy16OznGVWJtlw4RUf1PzkXd9nVQ==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@yuku-analyzer/binding-linux-arm-musl@0.9.4': + resolution: {integrity: sha512-y8TJui+PC2U/cdGpMXcnDkjLNdRhfsPPfFE7ynQ7uW3P4p5kyDqzvwQn44hAdKegJe52wZVS76oQa3GaMgEyrQ==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@yuku-analyzer/binding-linux-arm64-gnu@0.9.4': + resolution: {integrity: sha512-tp2MIesverwr8/bEurK30MrI0xefWY+F68blgOy9fT9tfQMQUeTqn9PuQIZY8KVFN+dzpnoq7OhQiEaQ+3qSFw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@yuku-analyzer/binding-linux-arm64-musl@0.9.4': + resolution: {integrity: sha512-4nNWWmnUU9vAT3hZyL8ZTc/yI7F5o64DDNwS20AhWyG8O0CRw2ACL9DTmZBjVhI9yJDCeX1Q/+FXy61EUVqczw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@yuku-analyzer/binding-linux-x64-gnu@0.9.4': + resolution: {integrity: sha512-GSkdo1mVakZaOA9YLP6m73Md3NUj7WydSTRUnxgOgqQT/mA86J5NxxRt6HN6HtY9oa5DOBPhAD5/HPn57hqhUQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@yuku-analyzer/binding-linux-x64-musl@0.9.4': + resolution: {integrity: sha512-R25p+uokKGZt52CeQvRk4BPayewDd+Rlo35VKQzxE5eHCtfAq0sblwbejJX0cEbnWIuXxC0u+XrthYZLVKs9zg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@yuku-analyzer/binding-win32-arm64@0.9.4': + resolution: {integrity: sha512-5FnK2lO3KJQ2TVSCkhuw0nETePAWXaR/tac+DxsfNZ1qCGKQCGAGsKyBahDr9Wur+bpKBjeZpRW3vihKAlsYYA==} + cpu: [arm64] + os: [win32] + + '@yuku-analyzer/binding-win32-x64@0.9.4': + resolution: {integrity: sha512-Zgdoy94SvUavdCkiHQVAiY3hV/snip8IubuMY18qA9UkJFzLPG14AKnUGJsS2PzIDlHZsn8EWVveaagbSalpBg==} + cpu: [x64] + os: [win32] + + '@yuku-toolchain/types@0.9.4': + resolution: {integrity: sha512-dqnMswJWE7YBbPEvGstgFlWFTRn20V/Hu82XTDjWxzaU18rGpxBqxhKlBlgyWrPARRvNLIqY2XA6S9edR+2AsA==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} + engines: {node: '>=0.4.0'} + hasBin: true + + astring@1.9.0: + resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} + hasBin: true + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + baseline-browser-mapping@2.11.20: + resolution: {integrity: sha512-H0ulySigv6icDJ1F7SjtdCD6PrhTpdYCmP0CactWy1+ekh0AFd0o1Wn5T8b+hnTmdBx19u9yhL6wvCylXMY7zw==} + engines: {node: '>=6.0.0'} + hasBin: true + + caniuse-lite@1.0.30001810: + resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + cn@0.2.6: + resolution: {integrity: sha512-+i4L0zUGgRcEnhsxueVrP7iBGxBx5iD0WOTYg1MwFEu2ZyCmH5Ov2V1cul2Ht5UchRQQgftCf4be/RxspuW6QQ==} + engines: {node: '>=20'} + hasBin: true + + collapse-white-space@2.1.0: + resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + compute-scroll-into-view@3.1.1: + resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + enhanced-resolve@5.24.5: + resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==} + engines: {node: '>=10.13.0'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + esast-util-from-estree@2.0.0: + resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} + + esast-util-from-js@2.0.1: + resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} + + esbuild@0.28.2: + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} + engines: {node: '>=18'} + hasBin: true + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + estree-util-attach-comments@3.0.0: + resolution: {integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==} + + estree-util-build-jsx@3.0.1: + resolution: {integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==} + + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + estree-util-scope@1.0.1: + resolution: {integrity: sha512-B0np3dcdxqILX5e9nEi5/Fr4K7gL4oYFVPV1zRa2e9wRCbQoZZNWOZFYyoInvXUPJXBXjss+QXlWLJChDEHDkA==} + + estree-util-to-js@2.0.0: + resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==} + + estree-util-value-to-estree@3.5.0: + resolution: {integrity: sha512-aMV56R27Gv3QmfmF1MY12GWkGzzeAezAX+UplqHVASfjc9wNzI/X6hC0S9oxq61WT4aQesLGslWP9tKk6ghRZQ==} + + estree-util-visit@2.0.0: + resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + framer-motion@13.1.1: + resolution: {integrity: sha512-B/xn2TPS4f61cEBLFjiYlQFnBZUW1YVj/LM+C+N4OP8Rs95VLEI2ot/RlfBg111la/EiyECFaJJi/A3FWA8MUA==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + + fumadocs-core@16.15.8: + resolution: {integrity: sha512-becV45sD2Mki57WgICAocwp0ufvydy3nj+AWIBnik7Im00MrAx81DW9a6lIa94NDSHGLZFNfDMdaNeHuUuya/A==} + peerDependencies: + '@mdx-js/mdx': '*' + '@mixedbread/sdk': 0.x.x + '@orama/core': 1.x.x + '@oramacloud/client': 2.x.x + '@tanstack/react-router': 1.x.x + '@types/estree-jsx': '*' + '@types/hast': '*' + '@types/mdast': '*' + '@types/react': '*' + algoliasearch: 5.x.x + flexsearch: '*' + lucide-react: '*' + next: 16.x.x + react: ^19.2.0 + react-dom: ^19.2.0 + react-router: 7.x.x || 8.x.x + waku: '*' + zod: 4.x.x + peerDependenciesMeta: + '@mdx-js/mdx': + optional: true + '@mixedbread/sdk': + optional: true + '@orama/core': + optional: true + '@oramacloud/client': + optional: true + '@tanstack/react-router': + optional: true + '@types/estree-jsx': + optional: true + '@types/hast': + optional: true + '@types/mdast': + optional: true + '@types/react': + optional: true + algoliasearch: + optional: true + flexsearch: + optional: true + lucide-react: + optional: true + next: + optional: true + react: + optional: true + react-dom: + optional: true + react-router: + optional: true + waku: + optional: true + zod: + optional: true + + fumadocs-mdx@15.4.0: + resolution: {integrity: sha512-bJCQfsckKUQ4x2pyz8OdASEAARVMB49kOej7njaJ3t+tYsP1HnwkmqiO8e/jdQHcv6JH3XkXWidTho3ZC/WBcA==} + hasBin: true + peerDependencies: + '@fumadocs/satteri': 0.x.x + '@types/mdast': '*' + '@types/mdx': '*' + '@types/react': '*' + fumadocs-core: ^16.15.3 + mdast-util-directive: '*' + next: ^15.3.0 || ^16.0.0 + react: ^19.2.0 + rolldown: '*' + satteri: ^0.10.5 + vite: 7.x.x || 8.x.x + peerDependenciesMeta: + '@fumadocs/satteri': + optional: true + '@types/mdast': + optional: true + '@types/mdx': + optional: true + '@types/react': + optional: true + mdast-util-directive: + optional: true + next: + optional: true + react: + optional: true + rolldown: + optional: true + satteri: + optional: true + vite: + optional: true + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + github-slugger@2.0.0: + resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + hast-util-from-parse5@8.0.3: + resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} + + hast-util-parse-selector@4.0.0: + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + + hast-util-raw@9.1.0: + resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} + + hast-util-to-estree@3.1.3: + resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-to-parse5@8.0.1: + resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hastscript@9.0.1: + resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + lucide-react@1.39.0: + resolution: {integrity: sha512-y8nXoEwvqqIsF927NBWXODa4bfMrcUeEb/9sgpwFqg0gUjgn3j5Hznk+v7STmPgZ2iQ11JKlbQGdFRuTOwvYkA==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magic-string@1.2.3: + resolution: {integrity: sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==} + + markdown-extensions@2.0.0: + resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} + engines: {node: '>=16'} + + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdx@3.0.0: + resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-extension-mdx-expression@3.0.1: + resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==} + + micromark-extension-mdx-jsx@3.0.2: + resolution: {integrity: sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==} + + micromark-extension-mdx-md@2.0.0: + resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==} + + micromark-extension-mdxjs-esm@3.0.0: + resolution: {integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==} + + micromark-extension-mdxjs@3.0.0: + resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-mdx-expression@2.0.3: + resolution: {integrity: sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-events-to-acorn@2.0.3: + resolution: {integrity: sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + + motion-dom@13.1.1: + resolution: {integrity: sha512-XSf8VYWSB6G/0IY3rWVbyLcxWXtAVHkN1PQE2agTaCv3u8RGvbwu56TyyR/MNzBqqNavEBTZzErcxI1TxBrjcA==} + + motion-utils@13.0.0: + resolution: {integrity: sha512-7DnN7TmbLcYXcG4RVadXIihWlyuM9afoUww8Y5Agg431kGKiuL2/OMyP4mJ5wLz+pvN3t5ySClLOaVXJ+wekRQ==} + + motion@13.1.1: + resolution: {integrity: sha512-WNZoK6xiF+kkTqkZ5K7FDDh6A8BG4i5Hc7KXtW8gtTxkpJFds+hIOrDaQGKjQj/AE/i4hJqAaUHEqp/Qo02y6Q==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + next-themes@0.4.6: + resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} + peerDependencies: + react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + + next@16.3.4: + resolution: {integrity: sha512-/Ztf6CeRH+ejEXUrYtqI4gkS66eFIHuSwqi60RgcpWKodxFZx2/dqVCMKBwILfAHXQ+F1b1vAudgj3mnxqtoIA==} + engines: {node: '>=20.9.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + + npm-to-yarn@3.2.0: + resolution: {integrity: sha512-K1HmQeZT2HrjpsR6KgqbN2FAXL2NrJJmNUSD9ck7HGTVu1JKXox8n9SB+tjbU8m8JGLF4OscrroPepew/L7/Xw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + oniguruma-parser@0.12.2: + resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} + + oniguruma-to-es@4.3.6: + resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} + + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + playwright-core@1.63.0: + resolution: {integrity: sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==} + engines: {node: '>=20'} + hasBin: true + + playwright@1.63.0: + resolution: {integrity: sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==} + engines: {node: '>=20'} + hasBin: true + + postcss@8.5.23: + resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.26: + resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} + engines: {node: ^10 || ^12 || >=14} + + property-information@7.2.0: + resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} + + react-dom@19.2.8: + resolution: {integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==} + peerDependencies: + react: ^19.2.8 + + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react@19.2.8: + resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==} + engines: {node: '>=0.10.0'} + + readdirp@5.1.1: + resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} + engines: {node: '>= 20.19.0'} + + recma-build-jsx@1.0.0: + resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} + + recma-jsx@1.0.1: + resolution: {integrity: sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + recma-parse@1.0.0: + resolution: {integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==} + + recma-stringify@1.0.0: + resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.1.0: + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + + rehype-raw@7.0.0: + resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + + rehype-recma@1.0.0: + resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-mdx@3.1.1: + resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + remark@15.0.1: + resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==} + + reselect@5.3.0: + resolution: {integrity: sha512-XGoLeRAVzUTcJ1qkxPQhDJyIZ5d6zzZD9nT7AEZOaaU9UbWclhycElmhO+VD5bFeLuzhPBaOV2oXC8uG35ZSpg==} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + scroll-into-view-if-needed@3.1.0: + resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + sharp@0.35.4: + resolution: {integrity: sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==} + engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + shiki@4.4.3: + resolution: {integrity: sha512-Mb/GvXPHBAXdgGIcnfU5L3ldpn1XcxrGkPHwqgRx17/I2XRfqlFKk2vGkHWINn1kdXvzJZeuO3is6I9KLPFm0g==} + engines: {node: '>=20'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + tailwindcss@4.3.3: + resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + tinyexec@1.3.0: + resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position-from-estree@2.0.0: + resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-remove-position@5.0.0: + resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + vfile-location@5.0.3: + resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + + yuku-analyzer@0.9.4: + resolution: {integrity: sha512-jbxxF5p6BgYhUd+gLv2mxtklqdTggJl8pa//od7McCmlbEps7/+O00kVrKjshcWw/HIHvqEtsYZaU+uTuXxjMA==} + + yuku-ast@0.9.4: + resolution: {integrity: sha512-AeeHDopMAy2mMafUMEIZxmQaCJOPEvBGDSSA23y5iKuzak3H2otfmgo4ObJubuzdHlJrv4eFY3KQ5XZOru5gpw==} + + zbsearch@4.0.0: + resolution: {integrity: sha512-gm4zfO31n2ZdruTTRQoWVWO4Q2+zrDt2GlrvIc+5JulRQNAm4IanCxER82vQ7Ug96FYkGqWUJBXFe1kGAcDWaQ==} + engines: {node: '>= 20.0.0'} + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@alloc/quick-lru@5.3.0': {} + + '@babel/runtime@7.29.7': {} + + '@base-ui/react@1.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@babel/runtime': 7.29.7 + '@base-ui/utils': 0.3.2(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@floating-ui/react-dom': 2.1.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@floating-ui/utils': 0.2.12 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + use-sync-external-store: 1.6.0(react@19.2.8) + optionalDependencies: + '@types/react': 19.2.18 + + '@base-ui/utils@0.3.2(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@babel/runtime': 7.29.7 + '@floating-ui/utils': 0.2.12 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + reselect: 5.3.0 + use-sync-external-store: 1.6.0(react@19.2.8) + optionalDependencies: + '@types/react': 19.2.18 + + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.28.2': + optional: true + + '@esbuild/android-arm64@0.28.2': + optional: true + + '@esbuild/android-arm@0.28.2': + optional: true + + '@esbuild/android-x64@0.28.2': + optional: true + + '@esbuild/darwin-arm64@0.28.2': + optional: true + + '@esbuild/darwin-x64@0.28.2': + optional: true + + '@esbuild/freebsd-arm64@0.28.2': + optional: true + + '@esbuild/freebsd-x64@0.28.2': + optional: true + + '@esbuild/linux-arm64@0.28.2': + optional: true + + '@esbuild/linux-arm@0.28.2': + optional: true + + '@esbuild/linux-ia32@0.28.2': + optional: true + + '@esbuild/linux-loong64@0.28.2': + optional: true + + '@esbuild/linux-mips64el@0.28.2': + optional: true + + '@esbuild/linux-ppc64@0.28.2': + optional: true + + '@esbuild/linux-riscv64@0.28.2': + optional: true + + '@esbuild/linux-s390x@0.28.2': + optional: true + + '@esbuild/linux-x64@0.28.2': + optional: true + + '@esbuild/netbsd-arm64@0.28.2': + optional: true + + '@esbuild/netbsd-x64@0.28.2': + optional: true + + '@esbuild/openbsd-arm64@0.28.2': + optional: true + + '@esbuild/openbsd-x64@0.28.2': + optional: true + + '@esbuild/openharmony-arm64@0.28.2': + optional: true + + '@esbuild/sunos-x64@0.28.2': + optional: true + + '@esbuild/win32-arm64@0.28.2': + optional: true + + '@esbuild/win32-ia32@0.28.2': + optional: true + + '@esbuild/win32-x64@0.28.2': + optional: true + + '@floating-ui/core@1.8.0': + dependencies: + '@floating-ui/utils': 0.2.12 + + '@floating-ui/dom@1.8.0': + dependencies: + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 + + '@floating-ui/react-dom@2.1.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@floating-ui/dom': 1.8.0 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + + '@floating-ui/utils@0.2.12': {} + + '@fuma-translate/react@1.0.2(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + optionalDependencies: + '@types/react': 19.2.18 + + '@fumadocs/base-ui@16.15.8(@types/mdx@2.0.14)(@types/react@19.2.18)(fumadocs-core@16.15.8(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.18)(lucide-react@1.39.0(react@19.2.8))(next@16.3.4(@playwright/test@1.63.0)(@types/node@26.3.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.4(@playwright/test@1.63.0)(@types/node@26.3.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.3.3)': + dependencies: + '@base-ui/react': 1.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@fuma-translate/react': 1.0.2(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@fumadocs/tailwind': 0.1.1(tailwindcss@4.3.3) + class-variance-authority: 0.7.1 + cn: 0.2.6 + fumadocs-core: 16.15.8(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.18)(lucide-react@1.39.0(react@19.2.8))(next@16.3.4(@playwright/test@1.63.0)(@types/node@26.3.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3) + lucide-react: 1.39.0(react@19.2.8) + motion: 13.1.1(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + next-themes: 0.4.6(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + react-remove-scroll: 2.7.2(@types/react@19.2.18)(react@19.2.8) + rehype-raw: 7.0.0 + scroll-into-view-if-needed: 3.1.0 + shiki: 4.4.3 + unist-util-visit: 5.1.0 + optionalDependencies: + '@types/mdx': 2.0.14 + '@types/react': 19.2.18 + next: 16.3.4(@playwright/test@1.63.0)(@types/node@26.3.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + transitivePeerDependencies: + - '@date-fns/tz' + - date-fns + - tailwindcss + + '@fumadocs/tailwind@0.1.1(tailwindcss@4.3.3)': + optionalDependencies: + tailwindcss: 4.3.3 + + '@fumari/image-size@0.1.0': {} + + '@img/colour@1.1.0': + optional: true + + '@img/sharp-darwin-arm64@0.35.4': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.3.3 + optional: true + + '@img/sharp-darwin-x64@0.35.4': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.3.3 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.4': + dependencies: + '@img/sharp-wasm32': 0.35.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.3.3': + optional: true + + '@img/sharp-libvips-darwin-x64@1.3.3': + optional: true + + '@img/sharp-libvips-linux-arm64@1.3.3': + optional: true + + '@img/sharp-libvips-linux-arm@1.3.3': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.3.3': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.3.3': + optional: true + + '@img/sharp-libvips-linux-s390x@1.3.3': + optional: true + + '@img/sharp-libvips-linux-x64@1.3.3': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.3.3': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.3.3': + optional: true + + '@img/sharp-linux-arm64@0.35.4': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.3.3 + optional: true + + '@img/sharp-linux-arm@0.35.4': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.3.3 + optional: true + + '@img/sharp-linux-ppc64@0.35.4': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.3.3 + optional: true + + '@img/sharp-linux-riscv64@0.35.4': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.3.3 + optional: true + + '@img/sharp-linux-s390x@0.35.4': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.3.3 + optional: true + + '@img/sharp-linux-x64@0.35.4': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.3.3 + optional: true + + '@img/sharp-linuxmusl-arm64@0.35.4': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.3.3 + optional: true + + '@img/sharp-linuxmusl-x64@0.35.4': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.3.3 + optional: true + + '@img/sharp-wasm32@0.35.4': + dependencies: + '@emnapi/runtime': 1.11.3 + optional: true + + '@img/sharp-webcontainers-wasm32@0.35.4': + dependencies: + '@img/sharp-wasm32': 0.35.4 + optional: true + + '@img/sharp-win32-arm64@0.35.4': + optional: true + + '@img/sharp-win32-ia32@0.35.4': + optional: true + + '@img/sharp-win32-x64@0.35.4': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.6.0': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.6.0 + + '@mdx-js/mdx@3.1.1': + dependencies: + '@types/estree': 1.0.9 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdx': 2.0.14 + acorn: 8.18.0 + collapse-white-space: 2.1.0 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-util-scope: 1.0.1 + estree-walker: 3.0.3 + hast-util-to-jsx-runtime: 2.3.6 + markdown-extensions: 2.0.0 + recma-build-jsx: 1.0.0 + recma-jsx: 1.0.1(acorn@8.18.0) + recma-stringify: 1.0.0 + rehype-recma: 1.0.0 + remark-mdx: 3.1.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + source-map: 0.7.6 + unified: 11.0.5 + unist-util-position-from-estree: 2.0.0 + unist-util-stringify-position: 4.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@next/env@16.3.4': {} + + '@next/swc-darwin-arm64@16.3.4': + optional: true + + '@next/swc-darwin-x64@16.3.4': + optional: true + + '@next/swc-linux-arm64-gnu@16.3.4': + optional: true + + '@next/swc-linux-arm64-musl@16.3.4': + optional: true + + '@next/swc-linux-x64-gnu@16.3.4': + optional: true + + '@next/swc-linux-x64-musl@16.3.4': + optional: true + + '@next/swc-win32-arm64-msvc@16.3.4': + optional: true + + '@next/swc-win32-x64-msvc@16.3.4': + optional: true + + '@playwright/test@1.63.0': + dependencies: + playwright: 1.63.0 + + '@shikijs/core@4.4.3': + dependencies: + '@shikijs/primitive': 4.4.3 + '@shikijs/types': 4.4.3 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@4.4.3': + dependencies: + '@shikijs/types': 4.4.3 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.6 + + '@shikijs/engine-oniguruma@4.4.3': + dependencies: + '@shikijs/types': 4.4.3 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@4.4.3': + dependencies: + '@shikijs/types': 4.4.3 + + '@shikijs/primitive@4.4.3': + dependencies: + '@shikijs/types': 4.4.3 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + + '@shikijs/themes@4.4.3': + dependencies: + '@shikijs/types': 4.4.3 + + '@shikijs/types@4.4.3': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@standard-schema/spec@1.1.0': {} + + '@swc/helpers@0.5.23': + dependencies: + tslib: 2.8.1 + + '@tailwindcss/node@4.3.3': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.24.5 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.3 + + '@tailwindcss/oxide-android-arm64@4.3.3': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.3': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.3': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + optional: true + + '@tailwindcss/oxide@4.3.3': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-x64': 4.3.3 + '@tailwindcss/oxide-freebsd-x64': 4.3.3 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.3 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.3 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-x64-musl': 4.3.3 + '@tailwindcss/oxide-wasm32-wasi': 4.3.3 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.3 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.3 + + '@tailwindcss/postcss@4.3.3': + dependencies: + '@alloc/quick-lru': 5.3.0 + '@tailwindcss/node': 4.3.3 + '@tailwindcss/oxide': 4.3.3 + postcss: 8.5.26 + tailwindcss: 4.3.3 + + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.9 + + '@types/estree@1.0.9': {} + + '@types/hast@3.0.5': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdx@2.0.14': {} + + '@types/ms@2.1.0': {} + + '@types/node@26.3.0': + dependencies: + undici-types: 8.3.0 + + '@types/react-dom@19.2.5(@types/react@19.2.18)': + dependencies: + '@types/react': 19.2.18 + + '@types/react@19.2.18': + dependencies: + csstype: 3.2.3 + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@ungap/structured-clone@1.4.0': {} + + '@yuku-analyzer/binding-android-arm64@0.9.4': + optional: true + + '@yuku-analyzer/binding-darwin-arm64@0.9.4': + optional: true + + '@yuku-analyzer/binding-darwin-x64@0.9.4': + optional: true + + '@yuku-analyzer/binding-freebsd-x64@0.9.4': + optional: true + + '@yuku-analyzer/binding-linux-arm-gnu@0.9.4': + optional: true + + '@yuku-analyzer/binding-linux-arm-musl@0.9.4': + optional: true + + '@yuku-analyzer/binding-linux-arm64-gnu@0.9.4': + optional: true + + '@yuku-analyzer/binding-linux-arm64-musl@0.9.4': + optional: true + + '@yuku-analyzer/binding-linux-x64-gnu@0.9.4': + optional: true + + '@yuku-analyzer/binding-linux-x64-musl@0.9.4': + optional: true + + '@yuku-analyzer/binding-win32-arm64@0.9.4': + optional: true + + '@yuku-analyzer/binding-win32-x64@0.9.4': + optional: true + + '@yuku-toolchain/types@0.9.4': {} + + acorn-jsx@5.3.2(acorn@8.18.0): + dependencies: + acorn: 8.18.0 + + acorn@8.18.0: {} + + astring@1.9.0: {} + + bail@2.0.2: {} + + baseline-browser-mapping@2.11.20: {} + + caniuse-lite@1.0.30001810: {} + + ccount@2.0.1: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + + chokidar@5.0.0: + dependencies: + readdirp: 5.1.1 + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + client-only@0.0.1: {} + + clsx@2.1.1: {} + + cn@0.2.6: {} + + collapse-white-space@2.1.0: {} + + comma-separated-tokens@2.0.3: {} + + compute-scroll-into-view@3.1.1: {} + + csstype@3.2.3: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + + dequal@2.0.3: {} + + detect-libc@2.1.2: {} + + detect-node-es@1.1.0: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + enhanced-resolve@5.24.5: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + entities@6.0.1: {} + + esast-util-from-estree@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + unist-util-position-from-estree: 2.0.0 + + esast-util-from-js@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + acorn: 8.18.0 + esast-util-from-estree: 2.0.0 + vfile-message: 4.0.3 + + esbuild@0.28.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.2 + '@esbuild/android-arm': 0.28.2 + '@esbuild/android-arm64': 0.28.2 + '@esbuild/android-x64': 0.28.2 + '@esbuild/darwin-arm64': 0.28.2 + '@esbuild/darwin-x64': 0.28.2 + '@esbuild/freebsd-arm64': 0.28.2 + '@esbuild/freebsd-x64': 0.28.2 + '@esbuild/linux-arm': 0.28.2 + '@esbuild/linux-arm64': 0.28.2 + '@esbuild/linux-ia32': 0.28.2 + '@esbuild/linux-loong64': 0.28.2 + '@esbuild/linux-mips64el': 0.28.2 + '@esbuild/linux-ppc64': 0.28.2 + '@esbuild/linux-riscv64': 0.28.2 + '@esbuild/linux-s390x': 0.28.2 + '@esbuild/linux-x64': 0.28.2 + '@esbuild/netbsd-arm64': 0.28.2 + '@esbuild/netbsd-x64': 0.28.2 + '@esbuild/openbsd-arm64': 0.28.2 + '@esbuild/openbsd-x64': 0.28.2 + '@esbuild/openharmony-arm64': 0.28.2 + '@esbuild/sunos-x64': 0.28.2 + '@esbuild/win32-arm64': 0.28.2 + '@esbuild/win32-ia32': 0.28.2 + '@esbuild/win32-x64': 0.28.2 + + escape-string-regexp@5.0.0: {} + + estree-util-attach-comments@3.0.0: + dependencies: + '@types/estree': 1.0.9 + + estree-util-build-jsx@3.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-walker: 3.0.3 + + estree-util-is-identifier-name@3.0.0: {} + + estree-util-scope@1.0.1: + dependencies: + '@types/estree': 1.0.9 + devlop: 1.1.0 + + estree-util-to-js@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + astring: 1.9.0 + source-map: 0.7.6 + + estree-util-value-to-estree@3.5.0: + dependencies: + '@types/estree': 1.0.9 + + estree-util-visit@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/unist': 3.0.3 + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + extend@3.0.2: {} + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + framer-motion@13.1.1(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + dependencies: + motion-dom: 13.1.1 + motion-utils: 13.0.0 + tslib: 2.8.1 + optionalDependencies: + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + + fumadocs-core@16.15.8(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.18)(lucide-react@1.39.0(react@19.2.8))(next@16.3.4(@playwright/test@1.63.0)(@types/node@26.3.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3): + dependencies: + '@fumari/image-size': 0.1.0 + estree-util-value-to-estree: 3.5.0 + github-slugger: 2.0.0 + hast-util-to-estree: 3.1.3 + hast-util-to-jsx-runtime: 2.3.6 + mdast-util-mdx: 3.0.0 + mdast-util-to-markdown: 2.1.2 + npm-to-yarn: 3.2.0 + remark: 15.0.1 + remark-gfm: 4.0.1 + remark-rehype: 11.1.2 + scroll-into-view-if-needed: 3.1.0 + shiki: 4.4.3 + tinyglobby: 0.2.17 + unified: 11.0.5 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + yaml: 2.9.0 + zbsearch: 4.0.0 + optionalDependencies: + '@mdx-js/mdx': 3.1.1 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@types/react': 19.2.18 + lucide-react: 1.39.0(react@19.2.8) + next: 16.3.4(@playwright/test@1.63.0)(@types/node@26.3.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + zod: 4.4.3 + transitivePeerDependencies: + - supports-color + + fumadocs-mdx@15.4.0(@types/mdast@4.0.4)(@types/mdx@2.0.14)(@types/react@19.2.18)(fumadocs-core@16.15.8(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.18)(lucide-react@1.39.0(react@19.2.8))(next@16.3.4(@playwright/test@1.63.0)(@types/node@26.3.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.4(@playwright/test@1.63.0)(@types/node@26.3.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8): + dependencies: + '@mdx-js/mdx': 3.1.1 + '@standard-schema/spec': 1.1.0 + chokidar: 5.0.0 + esbuild: 0.28.2 + estree-util-value-to-estree: 3.5.0 + fumadocs-core: 16.15.8(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.18)(lucide-react@1.39.0(react@19.2.8))(next@16.3.4(@playwright/test@1.63.0)(@types/node@26.3.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3) + github-slugger: 2.0.0 + magic-string: 1.2.3 + mdast-util-mdx: 3.0.0 + picocolors: 1.1.1 + picomatch: 4.0.7 + tinyexec: 1.3.0 + tinyglobby: 0.2.17 + unified: 11.0.5 + unist-util-remove-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + yaml: 2.9.0 + yuku-analyzer: 0.9.4 + zod: 4.4.3 + optionalDependencies: + '@types/mdast': 4.0.4 + '@types/mdx': 2.0.14 + '@types/react': 19.2.18 + next: 16.3.4(@playwright/test@1.63.0)(@types/node@26.3.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + react: 19.2.8 + transitivePeerDependencies: + - supports-color + + get-nonce@1.0.1: {} + + github-slugger@2.0.0: {} + + graceful-fs@4.2.11: {} + + hast-util-from-parse5@8.0.3: + dependencies: + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + devlop: 1.1.0 + hastscript: 9.0.1 + property-information: 7.2.0 + vfile: 6.0.3 + vfile-location: 5.0.3 + web-namespaces: 2.0.1 + + hast-util-parse-selector@4.0.0: + dependencies: + '@types/hast': 3.0.5 + + hast-util-raw@9.1.0: + dependencies: + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + '@ungap/structured-clone': 1.4.0 + hast-util-from-parse5: 8.0.3 + hast-util-to-parse5: 8.0.1 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + parse5: 7.3.0 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-estree@3.1.3: + dependencies: + '@types/estree': 1.0.9 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-attach-comments: 3.0.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + zwitch: 2.0.4 + transitivePeerDependencies: + - supports-color + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.9 + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-to-parse5@8.0.1: + dependencies: + '@types/hast': 3.0.5 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.5 + + hastscript@9.0.1: + dependencies: + '@types/hast': 3.0.5 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + + html-void-elements@3.0.0: {} + + inline-style-parser@0.2.7: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-decimal@2.0.1: {} + + is-hexadecimal@2.0.1: {} + + is-plain-obj@4.1.0: {} + + jiti@2.7.0: {} + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + longest-streak@3.1.0: {} + + lucide-react@1.39.0(react@19.2.8): + dependencies: + react: 19.2.8 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + magic-string@1.2.3: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + markdown-extensions@2.0.0: {} + + markdown-table@3.0.4: {} + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + mdast-util-from-markdown@2.0.3: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx@3.0.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.4.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-mdx-expression@3.0.1: + dependencies: + '@types/estree': 1.0.9 + devlop: 1.1.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-mdx-jsx@3.0.2: + dependencies: + '@types/estree': 1.0.9 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.3 + + micromark-extension-mdx-md@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-mdxjs-esm@3.0.0: + dependencies: + '@types/estree': 1.0.9 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.3 + + micromark-extension-mdxjs@3.0.0: + dependencies: + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) + micromark-extension-mdx-expression: 3.0.1 + micromark-extension-mdx-jsx: 3.0.2 + micromark-extension-mdx-md: 2.0.0 + micromark-extension-mdxjs-esm: 3.0.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-mdx-expression@2.0.3: + dependencies: + '@types/estree': 1.0.9 + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.3 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-events-to-acorn@2.0.3: + dependencies: + '@types/estree': 1.0.9 + '@types/unist': 3.0.3 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.3 + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.13 + debug: 4.4.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + motion-dom@13.1.1: + dependencies: + motion-utils: 13.0.0 + + motion-utils@13.0.0: {} + + motion@13.1.1(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + dependencies: + framer-motion: 13.1.1(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + tslib: 2.8.1 + optionalDependencies: + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + + ms@2.1.3: {} + + nanoid@3.3.18: {} + + next-themes@0.4.6(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + dependencies: + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + + next@16.3.4(@playwright/test@1.63.0)(@types/node@26.3.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + dependencies: + '@next/env': 16.3.4 + '@swc/helpers': 0.5.23 + baseline-browser-mapping: 2.11.20 + caniuse-lite: 1.0.30001810 + postcss: 8.5.23 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + styled-jsx: 5.1.6(react@19.2.8) + optionalDependencies: + '@next/swc-darwin-arm64': 16.3.4 + '@next/swc-darwin-x64': 16.3.4 + '@next/swc-linux-arm64-gnu': 16.3.4 + '@next/swc-linux-arm64-musl': 16.3.4 + '@next/swc-linux-x64-gnu': 16.3.4 + '@next/swc-linux-x64-musl': 16.3.4 + '@next/swc-win32-arm64-msvc': 16.3.4 + '@next/swc-win32-x64-msvc': 16.3.4 + '@playwright/test': 1.63.0 + sharp: 0.35.4(@types/node@26.3.0) + transitivePeerDependencies: + - '@babel/core' + - '@types/node' + - babel-plugin-macros + + npm-to-yarn@3.2.0: {} + + oniguruma-parser@0.12.2: {} + + oniguruma-to-es@4.3.6: + dependencies: + oniguruma-parser: 0.12.2 + regex: 6.1.0 + regex-recursion: 6.0.2 + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + picocolors@1.1.1: {} + + picomatch@4.0.7: {} + + playwright-core@1.63.0: {} + + playwright@1.63.0: + dependencies: + playwright-core: 1.63.0 + + postcss@8.5.23: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postcss@8.5.26: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + property-information@7.2.0: {} + + react-dom@19.2.8(react@19.2.8): + dependencies: + react: 19.2.8 + scheduler: 0.27.0 + + react-remove-scroll-bar@2.3.8(@types/react@19.2.18)(react@19.2.8): + dependencies: + react: 19.2.8 + react-style-singleton: 2.2.3(@types/react@19.2.18)(react@19.2.8) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.18 + + react-remove-scroll@2.7.2(@types/react@19.2.18)(react@19.2.8): + dependencies: + react: 19.2.8 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.18)(react@19.2.8) + react-style-singleton: 2.2.3(@types/react@19.2.18)(react@19.2.8) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.2.18)(react@19.2.8) + use-sidecar: 1.1.3(@types/react@19.2.18)(react@19.2.8) + optionalDependencies: + '@types/react': 19.2.18 + + react-style-singleton@2.2.3(@types/react@19.2.18)(react@19.2.8): + dependencies: + get-nonce: 1.0.1 + react: 19.2.8 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.18 + + react@19.2.8: {} + + readdirp@5.1.1: {} + + recma-build-jsx@1.0.0: + dependencies: + '@types/estree': 1.0.9 + estree-util-build-jsx: 3.0.1 + vfile: 6.0.3 + + recma-jsx@1.0.1(acorn@8.18.0): + dependencies: + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) + estree-util-to-js: 2.0.0 + recma-parse: 1.0.0 + recma-stringify: 1.0.0 + unified: 11.0.5 + + recma-parse@1.0.0: + dependencies: + '@types/estree': 1.0.9 + esast-util-from-js: 2.0.1 + unified: 11.0.5 + vfile: 6.0.3 + + recma-stringify@1.0.0: + dependencies: + '@types/estree': 1.0.9 + estree-util-to-js: 2.0.0 + unified: 11.0.5 + vfile: 6.0.3 + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + + rehype-raw@7.0.0: + dependencies: + '@types/hast': 3.0.5 + hast-util-raw: 9.1.0 + vfile: 6.0.3 + + rehype-recma@1.0.0: + dependencies: + '@types/estree': 1.0.9 + '@types/hast': 3.0.5 + hast-util-to-estree: 3.1.3 + transitivePeerDependencies: + - supports-color + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-mdx@3.1.1: + dependencies: + mdast-util-mdx: 3.0.0 + micromark-extension-mdxjs: 3.0.0 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + remark@15.0.1: + dependencies: + '@types/mdast': 4.0.4 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + reselect@5.3.0: {} + + scheduler@0.27.0: {} + + scroll-into-view-if-needed@3.1.0: + dependencies: + compute-scroll-into-view: 3.1.1 + + semver@7.8.5: + optional: true + + sharp@0.35.4(@types/node@26.3.0): + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.35.4 + '@img/sharp-darwin-x64': 0.35.4 + '@img/sharp-freebsd-wasm32': 0.35.4 + '@img/sharp-libvips-darwin-arm64': 1.3.3 + '@img/sharp-libvips-darwin-x64': 1.3.3 + '@img/sharp-libvips-linux-arm': 1.3.3 + '@img/sharp-libvips-linux-arm64': 1.3.3 + '@img/sharp-libvips-linux-ppc64': 1.3.3 + '@img/sharp-libvips-linux-riscv64': 1.3.3 + '@img/sharp-libvips-linux-s390x': 1.3.3 + '@img/sharp-libvips-linux-x64': 1.3.3 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.3 + '@img/sharp-libvips-linuxmusl-x64': 1.3.3 + '@img/sharp-linux-arm': 0.35.4 + '@img/sharp-linux-arm64': 0.35.4 + '@img/sharp-linux-ppc64': 0.35.4 + '@img/sharp-linux-riscv64': 0.35.4 + '@img/sharp-linux-s390x': 0.35.4 + '@img/sharp-linux-x64': 0.35.4 + '@img/sharp-linuxmusl-arm64': 0.35.4 + '@img/sharp-linuxmusl-x64': 0.35.4 + '@img/sharp-webcontainers-wasm32': 0.35.4 + '@img/sharp-win32-arm64': 0.35.4 + '@img/sharp-win32-ia32': 0.35.4 + '@img/sharp-win32-x64': 0.35.4 + '@types/node': 26.3.0 + optional: true + + shiki@4.4.3: + dependencies: + '@shikijs/core': 4.4.3 + '@shikijs/engine-javascript': 4.4.3 + '@shikijs/engine-oniguruma': 4.4.3 + '@shikijs/langs': 4.4.3 + '@shikijs/themes': 4.4.3 + '@shikijs/types': 4.4.3 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + + source-map-js@1.2.1: {} + + source-map@0.7.6: {} + + space-separated-tokens@2.0.2: {} + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + + styled-jsx@5.1.6(react@19.2.8): + dependencies: + client-only: 0.0.1 + react: 19.2.8 + + tailwindcss@4.3.3: {} + + tapable@2.3.3: {} + + tinyexec@1.3.0: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + + trim-lines@3.0.1: {} + + trough@2.2.0: {} + + tslib@2.8.1: {} + + typescript@6.0.3: {} + + undici-types@8.3.0: {} + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position-from-estree@2.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-remove-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-visit: 5.1.0 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + use-callback-ref@1.3.3(@types/react@19.2.18)(react@19.2.8): + dependencies: + react: 19.2.8 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.18 + + use-sidecar@1.1.3(@types/react@19.2.18)(react@19.2.8): + dependencies: + detect-node-es: 1.1.0 + react: 19.2.8 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.18 + + use-sync-external-store@1.6.0(react@19.2.8): + dependencies: + react: 19.2.8 + + vfile-location@5.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile: 6.0.3 + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + web-namespaces@2.0.1: {} + + yaml@2.9.0: {} + + yuku-analyzer@0.9.4: + dependencies: + '@yuku-toolchain/types': 0.9.4 + yuku-ast: 0.9.4 + optionalDependencies: + '@yuku-analyzer/binding-android-arm64': 0.9.4 + '@yuku-analyzer/binding-darwin-arm64': 0.9.4 + '@yuku-analyzer/binding-darwin-x64': 0.9.4 + '@yuku-analyzer/binding-freebsd-x64': 0.9.4 + '@yuku-analyzer/binding-linux-arm-gnu': 0.9.4 + '@yuku-analyzer/binding-linux-arm-musl': 0.9.4 + '@yuku-analyzer/binding-linux-arm64-gnu': 0.9.4 + '@yuku-analyzer/binding-linux-arm64-musl': 0.9.4 + '@yuku-analyzer/binding-linux-x64-gnu': 0.9.4 + '@yuku-analyzer/binding-linux-x64-musl': 0.9.4 + '@yuku-analyzer/binding-win32-arm64': 0.9.4 + '@yuku-analyzer/binding-win32-x64': 0.9.4 + + yuku-ast@0.9.4: + dependencies: + '@yuku-toolchain/types': 0.9.4 + + zbsearch@4.0.0: {} + + zod@4.4.3: {} + + zwitch@2.0.4: {} diff --git a/docs-site/pnpm-workspace.yaml b/docs-site/pnpm-workspace.yaml new file mode 100644 index 00000000..44e88bb2 --- /dev/null +++ b/docs-site/pnpm-workspace.yaml @@ -0,0 +1,15 @@ +packages: + - packages/* +autoInstallPeers: false +resolvePeersFromWorkspaceRoot: true +strictPeerDependencies: true +engineStrict: true +saveExact: true +allowBuilds: + esbuild: true +# Exact stable releases reviewed against upstream release 16.15.8 (2026-09-07). +# Keep the normal age gate for every other dependency. +minimumReleaseAgeStrict: true +minimumReleaseAgeExclude: + - '@fumadocs/base-ui@16.15.8' + - 'fumadocs-core@16.15.8' diff --git a/docs-site/postcss.config.mjs b/docs-site/postcss.config.mjs new file mode 100644 index 00000000..a34a3d56 --- /dev/null +++ b/docs-site/postcss.config.mjs @@ -0,0 +1,5 @@ +export default { + plugins: { + '@tailwindcss/postcss': {}, + }, +}; diff --git a/docs-site/public/assets/ack-social.png b/docs-site/public/assets/ack-social.png new file mode 100644 index 00000000..d7434197 Binary files /dev/null and b/docs-site/public/assets/ack-social.png differ diff --git a/docs-site/public/assets/favicon.svg b/docs-site/public/assets/favicon.svg new file mode 100644 index 00000000..6145dfea --- /dev/null +++ b/docs-site/public/assets/favicon.svg @@ -0,0 +1,7 @@ + + + diff --git a/docs-site/public/assets/logo-dark.svg b/docs-site/public/assets/logo-dark.svg new file mode 100644 index 00000000..b48f17c0 --- /dev/null +++ b/docs-site/public/assets/logo-dark.svg @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/docs-site/public/assets/logo-light.svg b/docs-site/public/assets/logo-light.svg new file mode 100644 index 00000000..733511aa --- /dev/null +++ b/docs-site/public/assets/logo-light.svg @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/docs-site/scripts/dev.mjs b/docs-site/scripts/dev.mjs new file mode 100644 index 00000000..1101834b --- /dev/null +++ b/docs-site/scripts/dev.mjs @@ -0,0 +1,71 @@ +import { spawn } from 'node:child_process'; +import { cp, rm, watch } from 'node:fs/promises'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const scriptDirectory = dirname(fileURLToPath(import.meta.url)); +const siteDirectory = resolve(scriptDirectory, '..'); +const sourceDirectory = resolve(siteDirectory, '../docs'); +const targetDirectory = resolve(siteDirectory, 'content/docs'); +const nextBinary = resolve(siteDirectory, 'node_modules/next/dist/bin/next'); +const abortController = new AbortController(); + +async function syncDocs() { + await rm(targetDirectory, { force: true, recursive: true }); + await cp(sourceDirectory, targetDirectory, { recursive: true }); +} + +await syncDocs(); + +const nextProcess = spawn(process.execPath, [nextBinary, 'dev'], { + cwd: siteDirectory, + env: process.env, + stdio: 'inherit', +}); + +let debounceTimer; +let syncQueue = Promise.resolve(); + +function queueSync() { + clearTimeout(debounceTimer); + debounceTimer = setTimeout(() => { + syncQueue = syncQueue + .then(syncDocs) + .catch((error) => { + console.error('[docs] Failed to refresh the content mirror.', error); + }); + }, 100); +} + +async function watchDocs() { + try { + for await (const _event of watch(sourceDirectory, { + recursive: true, + signal: abortController.signal, + })) { + queueSync(); + } + } catch (error) { + if (error?.name !== 'AbortError') throw error; + } +} + +watchDocs().catch((error) => { + console.error('[docs] Failed to watch the canonical documentation.', error); + nextProcess.kill('SIGTERM'); + process.exitCode = 1; +}); + +function stop(signal) { + abortController.abort(); + nextProcess.kill(signal); +} + +process.on('SIGINT', () => stop('SIGINT')); +process.on('SIGTERM', () => stop('SIGTERM')); + +nextProcess.on('exit', (code, signal) => { + abortController.abort(); + if (signal) process.kill(process.pid, signal); + else process.exit(code ?? 0); +}); diff --git a/docs-site/scripts/preview.py b/docs-site/scripts/preview.py new file mode 100644 index 00000000..57051cf6 --- /dev/null +++ b/docs-site/scripts/preview.py @@ -0,0 +1,43 @@ +"""Preview the static export locally, optionally mounted at /ack.""" +import argparse +import os +from functools import partial +from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from urllib.parse import urlsplit + +parser = argparse.ArgumentParser(description=__doc__) +parser.add_argument('--port', type=int, default=4173) +parser.add_argument('--base-path', default=os.environ.get('DOCS_BASE_PATH', '')) +args = parser.parse_args() +base = args.base_path.rstrip('/') +if base and (not base.startswith('/') or '..' in base or base.startswith('//')): + parser.error('base-path must be a path such as /ack') +root = Path(__file__).resolve().parents[1] / 'out' +if not root.is_dir(): + parser.error('Build the documentation before starting the preview.') + +class Handler(SimpleHTTPRequestHandler): + def translate_path(self, path): + pathname = urlsplit(path).path + if base: + if pathname != base and not pathname.startswith(base + '/'): + return str(root / '__outside_docs_mount__') + pathname = pathname[len(base):] or '/' + return super().translate_path(pathname) + + def guess_type(self, path): + if path.endswith('/api/search'): + return 'application/json' + if path.endswith('.md'): + return 'text/markdown' + return super().guess_type(path) + +server = ThreadingHTTPServer(('127.0.0.1', args.port), partial(Handler, directory=str(root))) +print(f'Preview: http://127.0.0.1:{args.port}{base}/', flush=True) +try: + server.serve_forever() +except KeyboardInterrupt: + pass +finally: + server.server_close() diff --git a/docs-site/scripts/sync-docs.mjs b/docs-site/scripts/sync-docs.mjs new file mode 100644 index 00000000..e9da3ac1 --- /dev/null +++ b/docs-site/scripts/sync-docs.mjs @@ -0,0 +1,10 @@ +import { cp, rm } from 'node:fs/promises'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const scriptDirectory = dirname(fileURLToPath(import.meta.url)); +const sourceDirectory = resolve(scriptDirectory, '../../docs'); +const targetDirectory = resolve(scriptDirectory, '../content/docs'); + +await rm(targetDirectory, { force: true, recursive: true }); +await cp(sourceDirectory, targetDirectory, { recursive: true }); diff --git a/docs-site/scripts/verify-export.py b/docs-site/scripts/verify-export.py new file mode 100644 index 00000000..657d6d74 --- /dev/null +++ b/docs-site/scripts/verify-export.py @@ -0,0 +1,100 @@ +"""Validate emitted routes, metadata, assets, and internal link fragments.""" +import json +import os +from html.parser import HTMLParser +from pathlib import Path +from urllib.parse import unquote, urljoin, urlsplit +from xml.etree import ElementTree + +root = Path(__file__).resolve().parents[1] / 'out' +site = os.environ['NEXT_PUBLIC_SITE_URL'].rstrip('/') +site_parts = urlsplit(site) +base = site_parts.path.rstrip('/') +errors = [] + +class Document(HTMLParser): + def __init__(self, text): + super().__init__(convert_charrefs=True) + self.ids, self.links, self.canonicals, self.images = set(), [], [], [] + self.feed(text) + + def handle_starttag(self, tag, attributes): + attrs = dict(attributes) + if attrs.get('id'): + self.ids.add(attrs['id']) + if tag == 'a' and attrs.get('href'): + self.links.append(attrs['href']) + if tag in ('img', 'script') and attrs.get('src'): + self.links.append(attrs['src']) + if tag == 'link' and attrs.get('rel') == 'stylesheet': + self.links.append(attrs['href']) + if tag == 'link' and attrs.get('rel') == 'canonical': + self.canonicals.append(attrs['href']) + if tag == 'meta' and attrs.get('property') == 'og:image': + self.images.append(attrs['content']) + +def emitted(pathname): + path = unquote(pathname) + if base: + if path != base and not path.startswith(base + '/'): + return None + path = path[len(base):] + candidate = (root / path.lstrip('/')).resolve() + if not candidate.is_relative_to(root.resolve()): + return None + for file in (candidate, candidate / 'index.html', candidate.with_suffix('.html')): + if file.is_file(): + return file + return None + +documents = {} +for file in root.rglob('*.html'): + if file.name == '404.html' or '_not-found' in file.parts or '404' in file.parts: + continue + relative = file.relative_to(root).as_posix() + route = '/' + (relative[:-10] if relative.endswith('index.html') else relative[:-5]) + documents[file.resolve()] = (site + route, Document(file.read_text())) + +if not documents: + errors.append('No exported HTML pages found.') +for file, (url, doc) in documents.items(): + for canonical in doc.canonicals: + if canonical.rstrip('/') != url.rstrip('/'): + errors.append(f'{file.relative_to(root)}: wrong canonical {canonical}; expected {url}') + for target in doc.links + doc.images: + resolved = urlsplit(urljoin(url, target)) + if resolved.scheme not in ('http', 'https') or resolved.netloc != site_parts.netloc: + continue + # Absolute links may intentionally leave this subpath (Concepta home). + if target.startswith(('http://', 'https://', '//')) and base and not (resolved.path == base or resolved.path.startswith(base + '/')): + continue + destination = emitted(resolved.path) + if destination is None: + errors.append(f'{file.relative_to(root)}: missing local target {target}') + elif resolved.fragment and destination in documents: + fragment = unquote(resolved.fragment) + if not fragment.startswith(':~:text=') and fragment not in documents[destination][1].ids: + errors.append(f'{file.relative_to(root)}: missing fragment {target}') + +for path in ['index.html', 'core-concepts/schemas/index.html', 'llms.txt', 'llms-full.txt', 'llms.mdx/core-concepts/schemas/content.md', 'og/core-concepts/schemas/image.png', 'sitemap.xml', 'robots.txt', 'api/search']: + file = root / path + if not file.is_file() or not file.stat().st_size: + errors.append(f'Missing export: {path}') + +if (root / 'sitemap.xml').is_file(): + tree = ElementTree.parse(root / 'sitemap.xml') + locations = [node.text for node in tree.findall('.//{*}loc')] + for location in locations: + if not location.startswith(site + '/') or emitted(urlsplit(location).path) is None: + errors.append(f'Invalid sitemap location: {location}') + if len(locations) != len(documents): + errors.append(f'Sitemap contains {len(locations)} pages; exported HTML has {len(documents)}.') +if (root / 'robots.txt').is_file() and f'Sitemap: {site}/sitemap.xml' not in (root / 'robots.txt').read_text(): + errors.append('Robots sitemap URL does not retain the deployment path.') +if (root / 'api/search').is_file(): + json.loads((root / 'api/search').read_text()) +if (root / 'llms.txt').is_file() and f'{site}/core-concepts/schemas' not in (root / 'llms.txt').read_text(): + errors.append('LLM index does not contain the canonical schema page URL.') +if errors: + raise SystemExit('\n'.join(sorted(set(errors)))) +print(f'Validated {len(documents)} documentation pages, links, fragments, metadata, and static endpoints at {site}.') diff --git a/docs-site/tests/browser/docs.spec.ts b/docs-site/tests/browser/docs.spec.ts new file mode 100644 index 00000000..52da7786 --- /dev/null +++ b/docs-site/tests/browser/docs.spec.ts @@ -0,0 +1,48 @@ +import { expect, test } from '@playwright/test'; + +const base = (process.env.DOCS_BASE_PATH ?? '').replace(/\/+$/, ''); +test('documentation renders and static endpoints work', async ({ page, request }) => { + const errors: string[] = []; + page.on('pageerror', (error) => errors.push(error.message)); + await page.goto(`${base}/core-concepts/schemas/`); + await expect(page.locator('h1')).toBeVisible(); + await expect(page.locator('a[href="https://github.com/conceptadev/ack"]').first()).toBeAttached(); + const markdown = await request.get(`${base}/llms.mdx/core-concepts/schemas/content.md`); + expect(markdown.ok()).toBeTruthy(); + expect(await markdown.text()).toContain('Ack'); + const image = await request.get(`${base}/og/core-concepts/schemas/image.png`); + expect(image.ok()).toBeTruthy(); + expect(image.headers()['content-type']).toContain('image/png'); + const missing = await request.get(`${base}/this-page-does-not-exist/`); + expect(missing.status()).toBe(404); + await page.emulateMedia({ colorScheme: 'dark' }); + await expect(page.locator('html')).toHaveClass(/dark/); + await page.emulateMedia({ colorScheme: 'light' }); + await expect(page.locator('html')).not.toHaveClass(/dark/); + expect(errors).toEqual([]); +}); + +test('keyboard search reads the static index and returns a working page', async ({ page }) => { + const failed: string[] = []; + page.on('pageerror', (error) => failed.push(error.message)); + await page.goto(`${base}/`); + await page.keyboard.press('Control+k'); + const dialog = page.getByRole('dialog'); + await expect(dialog).toBeVisible(); + await dialog.locator('input').first().fill('schemas'); + // Base UI search results are buttons that call the framework router. + const result = dialog.getByRole('button').filter({ + has: page.getByText('Schemas', { exact: true }), + }).first(); + try { + await expect(result).toBeVisible(); + await result.click(); + await expect(page).toHaveURL(new RegExp(`${base}/core-concepts/schemas`)); + await expect(page.locator('h1')).toBeVisible(); + expect(failed).toEqual([]); + } catch (error) { + console.error('Search dialog state:', await dialog.ariaSnapshot().catch(() => 'Dialog closed')); + console.error('Browser errors:', failed); + throw error; + } +}); diff --git a/docs-site/tests/contracts.test.mjs b/docs-site/tests/contracts.test.mjs new file mode 100644 index 00000000..78452493 --- /dev/null +++ b/docs-site/tests/contracts.test.mjs @@ -0,0 +1,34 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { readFileSync, existsSync } from 'node:fs'; +import { createRequire } from 'node:module'; +import { addBasePath, createSiteUrl, normalizeBasePath } from '../packages/docs-theme/src/urls.ts'; + +for (const [site, prefix] of [['https://concepta.dev', ''], ['https://concepta.dev/ack', '/ack'], ['https://concepta.dev/ack/', '/ack']]) { + test(`URL contract for ${site}`, () => { + for (const path of ['/', '/core-concepts/schemas', '/api/search', '/sitemap.xml', '/llms.txt', '/og/image.png', '/llms.mdx/content.md']) { + assert.equal(createSiteUrl(site, path), `https://concepta.dev${prefix}${path}`); + assert.equal(addBasePath(prefix, path), `${prefix}${path}`); + } + }); +} +test('reject unsafe and ambiguous paths', () => { + for (const path of ['//evil.example', '/../other', '/%2e%2e/other', '/a%2fb', '/a\\b']) assert.throws(() => createSiteUrl('https://concepta.dev/ack', path)); + for (const path of ['ack', '/ack?x=1', '//ack']) assert.throws(() => normalizeBasePath(path)); +}); +test('app and local theme resolve the same Base UI and React', () => { + const app = createRequire(new URL('../package.json', import.meta.url)); + const theme = createRequire(app.resolve('@conceptadev/docs-theme/package.json')); + for (const req of [app, theme]) { + const pkg = JSON.parse(readFileSync(req.resolve('fumadocs-ui/package.json'), 'utf8')); + assert.equal(pkg.name, '@fumadocs/base-ui'); + assert.equal(pkg.version, '16.15.8'); + } + assert.equal(app.resolve('react'), theme.resolve('react')); +}); +test('only one site implementation and one content pipeline exist', () => { + for (const path of ['app/page.tsx', 'app/docs', 'theme', 'scripts/sync-content.mjs']) assert.equal(existsSync(new URL(`../${path}`, import.meta.url)), false, path); + const manifest = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')); + assert.equal(manifest.scripts['docs:sync'], 'node scripts/sync-docs.mjs'); + assert.equal(manifest.scripts.start, undefined, 'next start cannot serve a static export'); +}); diff --git a/docs-site/tsconfig.json b/docs-site/tsconfig.json new file mode 100644 index 00000000..84b53a11 --- /dev/null +++ b/docs-site/tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "ignoreDeprecations": "6.0", + "paths": { + "@/*": ["./*"] + }, + "plugins": [ + { + "name": "next" + } + ] + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ], + "exclude": ["node_modules"] +} diff --git a/docs.json b/docs.json deleted file mode 100644 index 67245662..00000000 --- a/docs.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "$schema": "https://docs.page/schema.json", - "name": "Ack", - "description": "Documentation for the Ack project", - "socialPreview": "/assets/ack-social.png", - "favicon": "/assets/favicon.svg", - - "logo": { - "light": "/assets/logo-light.svg", - "dark": "/assets/logo-dark.svg" - }, - - "header": { - "showName": true - }, - "social": { - "github": "conceptadev/ack" - }, - "seo": { - "noindex": false - }, - "sidebar": [ - { - "group": "Getting Started", - "pages": [ - { "title": "Overview", "href": "/" }, - { "title": "Installation", "href": "/getting-started/installation" }, - { - "title": "Quickstart Tutorial", - "href": "/getting-started/quickstart-tutorial" - } - ] - }, - { - "group": "Essentials", - "pages": [ - { "title": "Schema Types", "href": "/core-concepts/schemas" }, - { "title": "Validation Rules", "href": "/core-concepts/validation" }, - { "title": "Error Handling", "href": "/core-concepts/error-handling" }, - { - "title": "JSON Serialization", - "href": "/core-concepts/json-serialization" - } - ] - }, - { - "group": "How-To Guides", - "pages": [ - { - "title": "Flutter Form Validation", - "href": "/guides/flutter-form-validation" - }, - { "title": "Common Recipes", "href": "/guides/common-recipes" }, - { "title": "Custom Validation", "href": "/guides/custom-validation" } - ] - }, - { - "group": "Advanced", - "pages": [ - { "title": "Codecs", "href": "/core-concepts/codecs" }, - { - "title": "Model Code Generation", - "href": "/core-concepts/typesafe-schemas" - }, - { - "title": "JSON Schema Integration", - "href": "/guides/json-schema-integration" - }, - { "title": "Configuration", "href": "/core-concepts/configuration" } - ] - }, - { - "group": "Adapter Authors", - "pages": [ - { - "title": "Adapter Package Quickstart", - "href": "/guides/schema-converter-quickstart" - }, - { - "title": "Creating Adapter Packages", - "href": "/guides/creating-schema-converter-packages" - } - ] - }, - { - "group": "Reference", - "pages": [{ "title": "API Reference", "href": "/api-reference/" }] - } - ], - "variables": { - "versions": { - "default": "1.2.0", - "isPrerelease": false - } - }, - "search": {}, - "content": { - "showPageTitle": true, - "zoomImages": true - } -} diff --git a/docs/architecture/ackinfer-model-generation.md b/docs/architecture/ackinfer-model-generation.md index 94eb10bd..2e3f78b0 100644 --- a/docs/architecture/ackinfer-model-generation.md +++ b/docs/architecture/ackinfer-model-generation.md @@ -1,4 +1,7 @@ -# Ack model and schema generation +--- +title: Ack model and schema generation +description: Architecture and generation contracts for AckInfer and AckModel. +--- `@AckInfer()` generates immutable Dart classes while the source Ack schema remains responsible for validation, defaults, codecs, and serialization. diff --git a/docs/core-concepts/schemas.mdx b/docs/core-concepts/schemas.mdx index 9acb50d3..f1f1998b 100644 --- a/docs/core-concepts/schemas.mdx +++ b/docs/core-concepts/schemas.mdx @@ -456,4 +456,4 @@ Every `safeParse()` returns a `SchemaResult` holding the validated value or a `S - **[Error handling](./error-handling.mdx)**: Handle validation errors effectively - **[Custom validation](../guides/custom-validation.mdx)**: Create custom constraints and refinements - **[JSON serialization](./json-serialization.mdx)**: Validate and transform JSON data -- **[Common recipes](../guides/common-recipes)**: Practical schema patterns +- **[Common recipes](../guides/common-recipes.mdx)**: Practical schema patterns diff --git a/docs/index.mdx b/docs/index.mdx index 35ee1ea9..4374c757 100644 --- a/docs/index.mdx +++ b/docs/index.mdx @@ -63,4 +63,4 @@ New here? The [Quickstart Tutorial](/getting-started/quickstart-tutorial) takes - [JSON Serialization](/core-concepts/json-serialization) — validate and encode JSON - [API Reference](/api-reference/) — a core API quick reference with a link to generated API docs -*Building with an AI agent? Start at [`llms.txt`](https://docs.page/conceptadev/ack/llms.txt) for a compact, machine-readable index.* +*Building with an AI agent? Start at [`llms.txt`](/llms.txt) for a compact, machine-readable index.* diff --git a/docs/meta.json b/docs/meta.json new file mode 100644 index 00000000..3c70f5b7 --- /dev/null +++ b/docs/meta.json @@ -0,0 +1,30 @@ +{ + "title": "Ack", + "pages": [ + "index", + "---Getting Started---", + "./getting-started/installation", + "./getting-started/quickstart-tutorial", + "---Essentials---", + "./core-concepts/schemas", + "./core-concepts/validation", + "./core-concepts/error-handling", + "./core-concepts/json-serialization", + "---How-To Guides---", + "./guides/flutter-form-validation", + "./guides/common-recipes", + "./guides/custom-validation", + "---Advanced---", + "./core-concepts/codecs", + "./core-concepts/typesafe-schemas", + "./guides/json-schema-integration", + "./core-concepts/configuration", + "---Adapter Authors---", + "./guides/schema-converter-quickstart", + "./guides/creating-schema-converter-packages", + "---Architecture---", + "./architecture/ackinfer-model-generation", + "---Reference---", + "./api-reference/index" + ] +} diff --git a/packages/ack/README.md b/packages/ack/README.md index de23a9c7..6ac19f8e 100644 --- a/packages/ack/README.md +++ b/packages/ack/README.md @@ -52,7 +52,7 @@ Use `.optional()` when a field may be omitted entirely. Chain `.nullable()` if a ## Documentation - [Full documentation](https://concepta.dev/ack) -- [AI agent index (llms.txt)](https://concepta.dev/documentation/ack/reference/llms-txt) +- [AI agent index (llms.txt)](https://concepta.dev/ack/llms.txt) ## Related Packages diff --git a/packages/ack/test/documentation/documentation_source_integrity_test.dart b/packages/ack/test/documentation/documentation_source_integrity_test.dart index a550a8db..89d6826c 100644 --- a/packages/ack/test/documentation/documentation_source_integrity_test.dart +++ b/packages/ack/test/documentation/documentation_source_integrity_test.dart @@ -90,7 +90,7 @@ void main() { }); test('converter authoring guides are published and navigable', () { - final docsConfig = jsonDecode(_readFromRepo('docs.json')) as Object?; + final docsConfig = jsonDecode(_readFromRepo('docs/meta.json')) as Object?; final serializedConfig = jsonEncode(docsConfig); expect( diff --git a/packages/ack_annotations/README.md b/packages/ack_annotations/README.md index 6625561a..0295422d 100644 --- a/packages/ack_annotations/README.md +++ b/packages/ack_annotations/README.md @@ -95,4 +95,4 @@ Unknown properties use `unknownProperties: AckUnknownPropertyPolicy.`. Use `discard` only for tolerant, read-only consumers; models that round-trip unknown keys use `capture` and may select their map field with `captureField`. `@AckField` can override `schema` and/or `AckFieldPresence`. See the -[Model Code Generation guide](https://concepta.dev/documentation/ack/advanced/typesafe-schemas). +[Model Code Generation guide](https://concepta.dev/ack/core-concepts/typesafe-schemas). diff --git a/packages/ack_generator/README.md b/packages/ack_generator/README.md index 7a38ae6f..9a863d54 100644 --- a/packages/ack_generator/README.md +++ b/packages/ack_generator/README.md @@ -145,7 +145,7 @@ getters. Classes, instance members, and local variables are rejected. `@AckModel()` annotates public, constructable `final class` declarations whose stored fields are final. Annotated sealed union bases remain supported, and their concrete branches must also be final. See the -[Model Code Generation guide](https://concepta.dev/documentation/ack/advanced/typesafe-schemas) +[Model Code Generation guide](https://concepta.dev/ack/core-concepts/typesafe-schemas) for both directions, field inference, sealed unions, passthrough properties, and build configuration. diff --git a/test/scripts/node_install_inline_policy_test.dart b/test/scripts/node_install_inline_policy_test.dart new file mode 100644 index 00000000..2214b602 --- /dev/null +++ b/test/scripts/node_install_inline_policy_test.dart @@ -0,0 +1,12 @@ +import 'package:test/test.dart'; + +import 'support/node_install_policy.dart'; + +void main() { + test('checks inline YAML steps as well as named run statements', () { + expect(isPinnedNodeInstall('- run: npm install unpinned'), isFalse); + expect(isPinnedNodeInstall('- run: pnpm install'), isFalse); + expect(isPinnedNodeInstall('- run: pnpm i --frozen-lockfile'), isTrue); + expect(isPinnedNodeInstall('- run: npm install tool@1.2.3'), isTrue); + }); +} diff --git a/test/scripts/node_install_policy_test.dart b/test/scripts/node_install_policy_test.dart new file mode 100644 index 00000000..a403353c --- /dev/null +++ b/test/scripts/node_install_policy_test.dart @@ -0,0 +1,35 @@ +import 'package:test/test.dart'; + +import 'support/node_install_policy.dart'; + +void main() { + test('allows frozen installs and exactly pinned package commands', () { + for (final command in [ + 'npm ci', + 'run: pnpm install --frozen-lockfile', + 'pnpm i --frozen-lockfile --ignore-scripts', + 'npm install --global pnpm@11.5.3', + r'npx --yes "@docs.page/cli@$DOCS_PAGE_CLI_VERSION" check', + '# npm install unpinned', + 'echo npm install is only an example', + ]) { + expect(isPinnedNodeInstall(command), isTrue, reason: command); + } + }); + + test('rejects unpinned installs without weakening the workflow guard', () { + for (final command in [ + 'npm install', + 'npm install --global pnpm', + 'npm i first@1.2.3 unpinned', + 'pnpm install', + 'pnpm i --no-frozen-lockfile', + 'pnpm install --frozen-lockfile --no-frozen-lockfile', + 'pnpm install package@1.2.3 --frozen-lockfile', + 'npx --yes some-cli', + 'echo ready && npm install unpinned', + ]) { + expect(isPinnedNodeInstall(command), isFalse, reason: command); + } + }); +} diff --git a/test/scripts/release_workflow_security_test.dart b/test/scripts/release_workflow_security_test.dart index 7eb5b781..2180497b 100644 --- a/test/scripts/release_workflow_security_test.dart +++ b/test/scripts/release_workflow_security_test.dart @@ -4,6 +4,8 @@ import 'dart:io'; import 'package:test/test.dart'; import 'package:yaml/yaml.dart'; +import 'support/node_install_policy.dart'; + void main() { group('every workflow and action', () { test('executes only immutable reviewed dependencies', () { @@ -62,24 +64,14 @@ void main() { }); test('installs only pinned npm packages', () { - // `npm ci` is exact by definition, because it reads the lockfile. Every - // other npm invocation must name a version, either literally or through - // an environment variable that the workflow sets. - final pinned = RegExp(r'@\d+\.\d+\.\d+|@\$[A-Z_]'); for (final entry in _automationSources().entries) { for (final line in entry.value.split('\n')) { - final statement = line.trim(); - if (statement.startsWith('#')) continue; - if (!statement.contains('npm install') && - !statement.contains('npx ')) { - continue; - } expect( - statement, - matches(pinned), + isPinnedNodeInstall(line), + isTrue, reason: - '${entry.key} installs an npm package without a version: ' - '$statement', + '${entry.key} must use a frozen lockfile or exact package ' + 'versions: ${line.trim()}', ); } } diff --git a/test/scripts/support/node_install_policy.dart b/test/scripts/support/node_install_policy.dart new file mode 100644 index 00000000..eefae5d1 --- /dev/null +++ b/test/scripts/support/node_install_policy.dart @@ -0,0 +1,40 @@ +/// Checks simple install commands in reviewed workflow run statements. +/// This is a policy check, not a general-purpose shell parser. +bool isPinnedNodeInstall(String statement) { + if (statement.trimLeft().startsWith('#')) return true; + final command = RegExp(r'^(?:-?\s*run:\s*)?(npm|pnpm|npx)\s+(.+)$'); + final pinned = RegExp( + r'^(@[^/\s]+/)?[^@\s]+@(\d+\.\d+\.\d+(-[\w.-]+)?|\$[A-Z_][A-Z_0-9]*)$', + ); + for (final part in statement.split(RegExp(r'&&|\|\||;|\|'))) { + final match = command.firstMatch(part.trim()); + if (match == null) continue; + final manager = match.group(1)!; + final tokens = [ + for (final token in match.group(2)!.split(RegExp(r'\s+'))) + token.replaceAll(RegExp(r'''^["']|["']$'''), ''), + ]; + final verb = tokens.first; + final arguments = tokens.skip(1).toList(); + if (manager == 'npm' && verb == 'ci') continue; + if (manager == 'npx') { + final packages = tokens.where((token) => !token.startsWith('-')); + if (packages.isEmpty || !pinned.hasMatch(packages.first)) return false; + continue; + } + if (!['install', 'i', 'add', 'dlx'].contains(verb)) continue; + final packages = arguments.where((token) => !token.startsWith('-')); + if (manager == 'pnpm' && ['install', 'i'].contains(verb)) { + if (packages.isNotEmpty || + !arguments.contains('--frozen-lockfile') || + arguments.contains('--no-frozen-lockfile')) { + return false; + } + continue; + } + if (packages.isEmpty || packages.any((token) => !pinned.hasMatch(token))) { + return false; + } + } + return true; +}