diff --git a/.claude/skills/curate-toolkit-docs/SKILL.md b/.claude/skills/curate-toolkit-docs/SKILL.md index cc82a3f98..69d0dd6d2 100644 --- a/.claude/skills/curate-toolkit-docs/SKILL.md +++ b/.claude/skills/curate-toolkit-docs/SKILL.md @@ -19,7 +19,7 @@ procedure. Curation is for prose bound to one toolkit or one tool: auth setup, enum value tables, parameter caveats, per-tool warnings. If the content is a standalone guide or concept explanation — anything a reader would reach from the sidebar -on its own — it belongs in `app/en/` as a normal MDX page instead. Stop and +on its own — it belongs in `content/docs/` as a normal MDX page instead. Stop and write that page. ## Step 2: read the neighbors @@ -116,7 +116,7 @@ cd toolkit-docs-generator ../node_modules/.bin/tsx src/cli/index.ts validate-curation --toolkit ``` -Errors name the exact file and reason. Then `pnpm vale:check` from the repo +Errors name the exact file and reason. Then `bun run vale:check` from the repo root. If you used `tool:`, confirm the value against the generated JSON, where diff --git a/.cursorignore b/.cursorignore index 3e23ba8cc..62b113f71 100644 --- a/.cursorignore +++ b/.cursorignore @@ -12,7 +12,6 @@ out/ toolkit-docs-generator/dist/ toolkit-docs-generator/output/ toolkit-docs-generator/test-output/ -.i18n-cache/ .pytest_cache/ *.log *.sqlite diff --git a/.github/scripts/update-example-dates.js b/.github/scripts/update-example-dates.js index 56d0ad8b5..aacf53a0b 100644 --- a/.github/scripts/update-example-dates.js +++ b/.github/scripts/update-example-dates.js @@ -16,21 +16,21 @@ function parseRepositoriesFromMDX(content) { let match = sampleAppCardRegex.exec(content); while (match !== null) { - const propsString = match[1]; + const [, propsString] = match; // Extract title and href from props const titleMatch = propsString.match(TITLE_PATTERN); const hrefMatch = propsString.match(HREF_PATTERN); if (titleMatch && hrefMatch) { - const title = titleMatch[1]; - const href = hrefMatch[1]; + const [, title] = titleMatch; + const [, href] = hrefMatch; // Check if it's a GitHub URL and extract repo name const githubMatch = href.match(GITHUB_PATTERN); if (githubMatch) { - const repoName = githubMatch[1]; + const [, repoName] = githubMatch; repositories.push({ title, repo: repoName, @@ -95,11 +95,18 @@ function formatDate(date) { async function updateExampleDates() { console.log("Parsing repositories from MDX file..."); - // Read the current MDX file - const mdxPath = path.join( - import.meta.dirname, - "../../app/en/resources/examples/page.mdx" - ); + // Read the current MDX file. The examples page is content/docs/resources/ + // examples.mdx, or examples/index.mdx once it gains child pages. + const contentDir = path.join(import.meta.dirname, "../../content/docs"); + const mdxPath = [ + path.join(contentDir, "resources/examples.mdx"), + path.join(contentDir, "resources/examples/index.mdx"), + ].find((candidate) => fs.existsSync(candidate)); + if (!mdxPath) { + throw new Error( + "Could not find the examples page under content/docs/resources/" + ); + } let content = fs.readFileSync(mdxPath, "utf8"); // Parse repositories from the MDX file diff --git a/.github/workflows/algolia-reindex.yml b/.github/workflows/algolia-reindex.yml index ace5bdbc2..6626f4d9a 100644 --- a/.github/workflows/algolia-reindex.yml +++ b/.github/workflows/algolia-reindex.yml @@ -46,7 +46,7 @@ jobs: id: changed run: | files=$(git diff-tree --no-commit-id --name-only -r HEAD) - if echo "$files" | grep -qE '^app/en/|^toolkit-docs-generator/data/toolkits/|^app/_components/toolkit-docs/|^app/_lib/toolkit-|^algolia/crawler-config\.js$|^scripts/sync-crawler-config\.ts$'; then + if echo "$files" | grep -qE '^content/docs/|^toolkit-docs-generator/data/toolkits/|^components/toolkit-docs/|^lib/toolkit-|^algolia/crawler-config\.js$|^scripts/sync-crawler-config\.ts$'; then echo "docs=true" >> "$GITHUB_OUTPUT" echo "Docs content changed — will reindex." else diff --git a/.github/workflows/check-external-urls.yml b/.github/workflows/check-external-urls.yml index 77264c8ea..e925c92ab 100644 --- a/.github/workflows/check-external-urls.yml +++ b/.github/workflows/check-external-urls.yml @@ -19,17 +19,13 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Install pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: - node-version: "22" - cache: pnpm + bun-version-file: package.json - name: Install dependencies - run: pnpm install + run: bun install --frozen-lockfile - name: Check external URLs - run: pnpm vitest run --config vitest.external-urls.config.ts + run: bun run test:external-urls diff --git a/.github/workflows/check-redirects.yml b/.github/workflows/check-redirects.yml index ccaa1bef8..c393dec80 100644 --- a/.github/workflows/check-redirects.yml +++ b/.github/workflows/check-redirects.yml @@ -4,8 +4,8 @@ on: pull_request: types: [opened, synchronize, reopened] paths: - - "app/**/*.md" - - "app/**/*.mdx" + - "content/docs/**/*.md" + - "content/docs/**/*.mdx" - "redirects.ts" permissions: @@ -26,19 +26,19 @@ jobs: - name: Fetch base branch run: git fetch origin ${{ github.base_ref }}:${{ github.base_ref }} - - name: Setup Node.js - uses: actions/setup-node@v4 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: - node-version: "20" + bun-version-file: package.json - name: Install dependencies - run: npm install -g pnpm && pnpm install + run: bun install --frozen-lockfile - name: Check for missing redirects id: check run: | set -o pipefail - pnpm check-redirects ${{ github.base_ref }} 2>&1 | tee redirect-check-output.txt + bun run check-redirects ${{ github.base_ref }} 2>&1 | tee redirect-check-output.txt continue-on-error: true - name: Comment on PR if redirects are missing @@ -52,7 +52,7 @@ jobs: // Extract the missing redirects and suggestions from output const body = `## 🔗 Missing Redirects Detected - This PR deletes markdown files that don't have corresponding redirects in \`redirects.ts\`. + This PR deletes content pages that don't have corresponding redirects in \`redirects.ts\`. When you delete a page, you must add a redirect to prevent broken links for users who have bookmarked the old URL. diff --git a/.github/workflows/generate-toolkit-docs.yml b/.github/workflows/generate-toolkit-docs.yml index 23ef60070..1d246c85e 100644 --- a/.github/workflows/generate-toolkit-docs.yml +++ b/.github/workflows/generate-toolkit-docs.yml @@ -2,7 +2,7 @@ name: Generate toolkit docs # Description: Generate toolkit JSON from the public tool catalog, then sync sidebar navigation. # Flow: # 1) Generate toolkit JSON into toolkit-docs-generator/data/toolkits -# 2) Sync integrations sidebar _meta.tsx from toolkit-docs-generator/data/toolkits +# 2) Sync integrations sidebar meta.json files from toolkit-docs-generator/data/toolkits # 3) Regenerate public/llms.txt so the automation PR is self-contained # 4) Create or update a PR if changes were produced # @@ -52,22 +52,25 @@ jobs: with: fetch-depth: 0 - - name: Install pnpm - uses: pnpm/action-setup@v4 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version-file: package.json + # The generator runs on Node through tsx: Bun resolves the design system's + # `bun` export condition, which points at sources the package does not ship. - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: "22" - cache: pnpm + node-version-file: .nvmrc - name: Install dependencies - run: pnpm install --frozen-lockfile + run: bun install --frozen-lockfile - name: Generate toolkit docs id: generate-docs - # Invoked by path rather than through `pnpm exec`, which would reset the - # working directory to the repo root and break the relative paths below. + # Invoked by path from the generator directory so the relative paths + # below resolve against it. run: | ../node_modules/.bin/tsx src/cli/index.ts generate \ --all \ @@ -99,12 +102,12 @@ jobs: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - name: Sync toolkit sidebar navigation - run: pnpm exec tsx toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts --remove-empty-sections=false --verbose + run: bun run sync-toolkit-sidebar --remove-empty-sections=false --verbose # GITHUB_TOKEN PRs do not start llmstxt.yml, so generate the file here. # The toolkit generator CLI still does not write llms.txt. - name: Generate llms.txt - run: pnpm llmstxt + run: bun run llmstxt env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} diff --git a/.github/workflows/llmstxt.yml b/.github/workflows/llmstxt.yml index 3e2d9b051..b0e6dc596 100644 --- a/.github/workflows/llmstxt.yml +++ b/.github/workflows/llmstxt.yml @@ -6,15 +6,15 @@ on: branches: - main paths: - - "app/en/**/*.mdx" - - "app/en/**/_meta.tsx" + - "content/docs/**/*.mdx" + - "content/docs/**/meta.json" - "scripts/generate-llmstxt.ts" - "toolkit-docs-generator/data/toolkits/**" pull_request: types: [opened, synchronize, reopened] paths: - - "app/en/**/*.mdx" - - "app/en/**/_meta.tsx" + - "content/docs/**/*.mdx" + - "content/docs/**/meta.json" - "scripts/generate-llmstxt.ts" - "toolkit-docs-generator/data/toolkits/**" @@ -28,24 +28,28 @@ jobs: runs-on: ubuntu-latest steps: - - name: Use Node.js - uses: actions/setup-node@v4 - with: - node-version: "22.x" - - name: Checkout code uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.ref || github.ref }} - - name: Install dependencies - run: npm install -g pnpm + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version-file: package.json - - name: Install pnpm - run: pnpm install + # The script runs on Node through tsx: Bun resolves the design system's + # `bun` export condition, which points at sources the package does not ship. + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + + - name: Install dependencies + run: bun install --frozen-lockfile - name: generate LLMs.txt - run: pnpm llmstxt + run: bun run llmstxt env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} diff --git a/.github/workflows/style-review.yml b/.github/workflows/style-review.yml index bd013d593..e7933d30d 100644 --- a/.github/workflows/style-review.yml +++ b/.github/workflows/style-review.yml @@ -10,8 +10,8 @@ on: pull_request: types: [opened, synchronize, reopened] paths: - - "app/en/**/*.md" - - "app/en/**/*.mdx" + - "content/docs/**/*.md" + - "content/docs/**/*.mdx" permissions: contents: read @@ -37,16 +37,13 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Use Node.js - uses: actions/setup-node@v4 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: - node-version: "22.x" - - - name: Install pnpm - run: npm install -g pnpm + bun-version-file: package.json - name: Install dependencies - run: pnpm install + run: bun install --frozen-lockfile - name: Install Vale run: | @@ -56,7 +53,7 @@ jobs: run: vale sync - name: Run style review - run: pnpm vale:review --pr ${{ github.event.pull_request.number || inputs.pr_number }} + run: bun run vale:review --pr ${{ github.event.pull_request.number || inputs.pr_number }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} diff --git a/.github/workflows/style-structural-review.yml b/.github/workflows/style-structural-review.yml index 7b70aa730..e597ce213 100644 --- a/.github/workflows/style-structural-review.yml +++ b/.github/workflows/style-structural-review.yml @@ -10,8 +10,8 @@ on: pull_request: types: [closed] paths: - - "app/en/**/*.md" - - "app/en/**/*.mdx" + - "content/docs/**/*.md" + - "content/docs/**/*.mdx" permissions: contents: write @@ -30,16 +30,13 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Use Node.js - uses: actions/setup-node@v4 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: - node-version: "22.x" - - - name: Install pnpm - run: npm install -g pnpm + bun-version-file: package.json - name: Install dependencies - run: pnpm install + run: bun install --frozen-lockfile - name: Install Vale run: | @@ -49,7 +46,7 @@ jobs: run: vale sync - name: Run editorial review - run: pnpm vale:editorial --pr ${{ github.event.pull_request.number || inputs.pr_number }} + run: bun run vale:editorial --pr ${{ github.event.pull_request.number || inputs.pr_number }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} diff --git a/.github/workflows/sync-crawler-config.yml b/.github/workflows/sync-crawler-config.yml index 31eb7a38d..2f717f567 100644 --- a/.github/workflows/sync-crawler-config.yml +++ b/.github/workflows/sync-crawler-config.yml @@ -36,10 +36,13 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: - node-version-file: .nvmrc + bun-version-file: package.json + + - name: Install dependencies + run: bun install --frozen-lockfile - name: Sync config and trigger reindex env: ALGOLIA_CRAWLER_ID: ${{ vars.ALGOLIA_CRAWLER_ID }} @@ -50,4 +53,4 @@ jobs: # Vercel production deploy so pages are live before the crawl starts. # On workflow_dispatch there is no pending deploy, so reindex immediately. SKIP_REINDEX: ${{ github.event_name == 'push' }} - run: pnpm sync-crawler-config + run: bun run sync-crawler-config diff --git a/.github/workflows/sync-helm-chart-docs.yml b/.github/workflows/sync-helm-chart-docs.yml index ca5f9dd67..b75d74247 100644 --- a/.github/workflows/sync-helm-chart-docs.yml +++ b/.github/workflows/sync-helm-chart-docs.yml @@ -48,17 +48,21 @@ jobs: with: fetch-depth: 0 - - name: Install pnpm - uses: pnpm/action-setup@v4 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version-file: package.json + # llms.txt generation runs on Node through tsx: Bun resolves the design + # system's `bun` export condition, which points at sources the package + # does not ship. - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: "22" - cache: pnpm + node-version-file: .nvmrc - name: Install dependencies - run: pnpm install --frozen-lockfile + run: bun install --frozen-lockfile - name: Install Helm uses: azure/setup-helm@v4 @@ -79,11 +83,11 @@ jobs: exit 1 - name: Sync values reference and examples - run: pnpm exec tsx scripts/sync-helm-chart-docs.ts --chart-dir "$RUNNER_TEMP/chart/arcade" --version "$VERSION" + run: bun scripts/sync-helm-chart-docs.ts --chart-dir "$RUNNER_TEMP/chart/arcade" --version "$VERSION" # GITHUB_TOKEN PRs do not start llmstxt.yml, so generate the file here. - name: Generate llms.txt - run: pnpm llmstxt + run: bun run llmstxt env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 147e17586..012e56b13 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -14,38 +14,30 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Setup Node.js - uses: actions/setup-node@v4 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: - node-version: '22.x' + bun-version-file: package.json - - name: Install pnpm - run: npm install -g pnpm - - - name: Get pnpm store directory - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - - name: Setup pnpm cache - uses: actions/cache@v3 + - name: Setup Bun cache + uses: actions/cache@v4 with: - path: ${{ env.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} restore-keys: | - ${{ runner.os }}-pnpm-store- + ${{ runner.os }}-bun- - name: Install dependencies - run: pnpm install + run: bun install --frozen-lockfile - name: Run linter - run: pnpm run lint + run: bun run lint - name: Run typecheck - run: pnpm run typecheck + run: bun run typecheck - name: Try a build - run: pnpm build + run: bun run build - name: Run tests - run: pnpm test + run: bun run test diff --git a/.github/workflows/update-design-system-dependency.yml b/.github/workflows/update-design-system-dependency.yml index e64d09123..316eb7a61 100644 --- a/.github/workflows/update-design-system-dependency.yml +++ b/.github/workflows/update-design-system-dependency.yml @@ -24,18 +24,16 @@ jobs: with: fetch-depth: 0 - - name: Install pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: - node-version: "22" - cache: pnpm + bun-version-file: package.json - name: Install dependencies - run: pnpm install --frozen-lockfile + run: bun install --frozen-lockfile + # `--exact` keeps the pinned-version policy (no caret ranges); bunfig's + # minimumReleaseAge still rejects a release younger than three days. # The monorepo dispatches this workflow seconds after `bun publish` # returns, but the npm registry takes several minutes to serve the new # version. Resolving `latest` right away picks up the previous release, @@ -63,12 +61,12 @@ jobs: - name: Update @arcadeai/design-system env: VERSION: ${{ inputs.version || 'latest' }} - run: pnpm install "@arcadeai/design-system@$VERSION" + run: bun add --exact "@arcadeai/design-system@$VERSION" - name: Check for changes id: check-changes run: | - if [ -n "$(git status --porcelain package.json pnpm-lock.yaml)" ]; then + if [ -n "$(git status --porcelain package.json bun.lock)" ]; then echo "has_changes=true" >> "$GITHUB_OUTPUT" else echo "has_changes=false" >> "$GITHUB_OUTPUT" @@ -77,8 +75,8 @@ jobs: - name: Run design-system compatibility tests if: steps.check-changes.outputs.has_changes == 'true' run: | - pnpm vitest run toolkit-docs-generator/tests/scripts/sync-toolkit-sidebar.test.ts toolkit-docs-generator/tests/sources/oauth-provider-resolver.test.ts - pnpm run build + bunx vitest run toolkit-docs-generator/tests/scripts/sync-toolkit-sidebar.test.ts toolkit-docs-generator/tests/sources/oauth-provider-resolver.test.ts + bun run build # Compatibility tests and a production build run above, so this workflow # does not depend on Test starting on the opened PR. @@ -103,7 +101,7 @@ jobs: - Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} add-paths: | package.json - pnpm-lock.yaml + bun.lock branch: automation/update-design-system-dependency delete-branch: true diff --git a/.github/workflows/update-example-dates.yml b/.github/workflows/update-example-dates.yml index 4b9d88e7f..70b126ab4 100644 --- a/.github/workflows/update-example-dates.yml +++ b/.github/workflows/update-example-dates.yml @@ -17,16 +17,16 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} fetch-depth: 1 - - name: Setup Node.js - uses: actions/setup-node@v4 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: - node-version: '22.x' + bun-version-file: package.json - name: Update repository dates env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - node .github/scripts/update-example-dates.js + bun .github/scripts/update-example-dates.js - name: Check for changes id: check-changes diff --git a/.gitignore b/.gitignore index 596a0104d..d5e436d7f 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ toolkit-docs-generator-verification/logs/ # Git worktrees .worktrees/ .cursor/* + +# Fumadocs generated sources +.source diff --git a/.husky/pre-commit b/.husky/pre-commit index a55f31a75..6965ed63c 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -18,41 +18,41 @@ if [ -n "$STAGED_DOCS" ]; then # Run vale-fix script in staged mode (interactive) if [ -t 0 ]; then # Terminal is interactive - run the fix script - pnpm vale:fix --staged + bun run vale:fix --staged else # Non-interactive (CI) - just run vale and report echo "$STAGED_DOCS" | xargs vale --output=line || { - echo "⚠️ Vale found style issues. Run 'pnpm vale:fix ' to fix." + echo "⚠️ Vale found style issues. Run 'bun run vale:fix ' to fix." # Don't block commit for style issues in CI, only toxic language # The GitHub Action (Option 1) will handle this later } fi fi -# --- Check Meta Keys (when _meta.tsx files are changed) --- -STAGED_META=$(git diff --cached --name-only --diff-filter=ACMR | grep -E '_meta\.tsx$' || true) +# --- Check Meta Keys (when content/docs meta.json files are changed) --- +STAGED_META=$(git diff --cached --name-only --diff-filter=ACMR | grep -E '^content/docs/(.*/)?meta\.json$' || true) if [ -n "$STAGED_META" ]; then - echo "🔍 Checking _meta.tsx keys..." + echo "🔍 Checking meta.json pages..." - if ! pnpm check-meta --staged-only; then + if ! bun run check-meta --staged-only; then echo "" - echo "❌ Commit blocked: _meta.tsx keys must match sibling directories or files." + echo "❌ Commit blocked: meta.json \"pages\" entries must match sibling pages or folders." exit 1 fi fi -# --- Check Redirects (when markdown pages are deleted or renamed) --- +# --- Check Redirects (when content pages are deleted or renamed) --- # Detect deleted pages (D status) and renamed pages (R status - old path needs redirect) -DELETED_PAGES=$(git diff --cached --name-status | grep -E "^D.*page\.(md|mdx)$" | cut -f2 || true) -RENAMED_PAGES=$(git diff --cached --name-status | grep -E "^R.*page\.(md|mdx)$" | cut -f2 || true) +DELETED_PAGES=$(git diff --cached --name-status | grep -E "^D.*content/docs/.*\.(md|mdx)$" | cut -f2 || true) +RENAMED_PAGES=$(git diff --cached --name-status | grep -E "^R.*content/docs/.*\.(md|mdx)$" | cut -f2 || true) if [ -n "$DELETED_PAGES" ] || [ -n "$RENAMED_PAGES" ]; then echo "🔗 Detected deleted/renamed page(s), checking for redirects..." # Run the TypeScript redirect checker with auto-fix (only checks staged changes) # This will add redirect entries to redirects.ts if missing - if ! pnpm check-redirects --auto-fix --staged-only 2>&1; then + if ! bun run check-redirects --auto-fix --staged-only 2>&1; then # Stage redirects.ts if it was modified if git diff --name-only redirects.ts 2>/dev/null | grep -q "redirects.ts"; then git add redirects.ts @@ -76,14 +76,19 @@ fi if git diff --cached --name-only | grep -q "redirects.ts"; then echo "🔗 Updating internal links for new redirects..." + # Files scanned by scripts/update-internal-links.ts (keep in sync with LINK_FILE_GLOBS there) + LINK_PATHS="content/docs/**/*.mdx content/docs/**/*.md components/**/*.tsx app/**/*.tsx lib/**/*.ts lib/**/*.tsx data/**/*.ts" + # Capture files already modified in working directory BEFORE running update-links # This prevents accidentally staging unrelated changes the user has in progress - ALREADY_MODIFIED=$(git diff --name-only -- 'app/**/*.mdx' 'app/**/*.tsx' 'app/**/*.md' 2>/dev/null || true) + # shellcheck disable=SC2086 + ALREADY_MODIFIED=$(git diff --name-only -- $LINK_PATHS 2>/dev/null || true) # Run the TypeScript update script - if pnpm update-links 2>/dev/null; then + if bun run update-links 2>/dev/null; then # Find files modified AFTER running update-links - NOW_MODIFIED=$(git diff --name-only -- 'app/**/*.mdx' 'app/**/*.tsx' 'app/**/*.md' 2>/dev/null || true) + # shellcheck disable=SC2086 + NOW_MODIFIED=$(git diff --name-only -- $LINK_PATHS 2>/dev/null || true) # Only stage files that were NOT already modified (i.e., newly modified by update-links) STAGED_COUNT=0 @@ -122,4 +127,4 @@ if git diff --cached --name-only | grep -q "redirects.ts"; then fi # --- Lint Staged (formatting) --- -pnpm exec lint-staged +bunx lint-staged diff --git a/.i18n-cache/hashes.json b/.i18n-cache/hashes.json deleted file mode 100644 index 7c49a0425..000000000 --- a/.i18n-cache/hashes.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "_metadata": { - "version": "1.0.0", - "created": "2025-09-30T08:04:53.155Z", - "updated": "2025-09-30T14:53:21.378Z" - }, - "data": { - "es": {}, - "pt-BR": {} - } -} diff --git a/.vscode/launch.json b/.vscode/launch.json index dda92591e..8ef288b4d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "name": "Arcade Docs Site", "type": "node-terminal", "request": "launch", - "command": "pnpm dev", + "command": "bun run dev", "serverReadyAction": { "pattern": "Local:.+(http?://\\S+)", "uriFormat": "%s", diff --git a/.vscode/settings.json b/.vscode/settings.json index de6d9c1c7..4ec5c003e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "cSpell.words": [ + "fumadocs", "httpx", - "nextra", "posthog", "toolkits" ], diff --git a/AGENTS.md b/AGENTS.md index 9faea8f93..7dda14f6e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,12 +1,16 @@ # Agent Instructions -When working on documentation in this repo, follow the writing standards in [STYLEGUIDE.md](./STYLEGUIDE.md). +When working on documentation in this repo, follow the writing standards in [STYLEGUIDE.md](./STYLEGUIDE.md) and the formatting conventions in [AUTHORING.md](./AUTHORING.md). Repo layout, commands, and tooling rules are in [CLAUDE.md](./CLAUDE.md). ## Quick Reference -- Run `pnpm vale:check` to check all docs for style issues -- Run `pnpm vale:fix ` to fix issues with AI assistance (optional, requires API key) -- Run `pnpm vale:sync` to update Vale style packages +- Content lives in `content/docs/**/*.mdx`; sidebar order lives in each folder's `meta.json` +- Run `bun run vale:check` to check all docs for style issues +- Run `bun run vale:fix ` to fix issues with AI assistance (optional, requires API key) +- Run `bun run vale:sync` to update Vale style packages +- Run `bun run check-meta` after adding, renaming, or removing a page +- Run `bun run check-redirects` after deleting or moving a page (then fill in `redirects.ts`) +- Use `bun`/`bunx` for everything; never `pnpm`, `npm`, `npx`, or `tsx` ## Automated Style Reviews @@ -14,7 +18,7 @@ This repo has automated style review workflows that run on PRs: ### Style Review (on PR open/update) -When you open or update a PR with changes to `app/en/**/*.md` or `app/en/**/*.mdx`, the style review workflow: +When you open or update a PR with changes to `content/docs/**/*.md` or `content/docs/**/*.mdx`, the style review workflow: 1. Runs Vale on changed files 2. Sends issues to an LLM for suggested fixes @@ -53,10 +57,10 @@ If both keys are present, Anthropic is used by default. ### Commands ```bash -pnpm vale:check # Check all docs for style issues -pnpm vale:fix # Interactive AI-powered fixes -pnpm vale:review --pr N # Run style review locally on PR #N -pnpm vale:editorial --pr N # Run editorial review locally on merged PR #N +bun run vale:check # Check all docs for style issues +bun run vale:fix # Interactive AI-powered fixes +bun run vale:review --pr N # Run style review locally on PR #N +bun run vale:editorial --pr N # Run editorial review locally on merged PR #N ``` ### Without an API Key @@ -67,3 +71,13 @@ If you don't have an API key, `vale:fix` will still: - Flag any toxic language issues that must be fixed You can then fix issues manually using the detailed output from `vale `. + + + +# This is NOT the Next.js you know + +This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices. + +This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean. + + diff --git a/AUTHORING.md b/AUTHORING.md index c4d96173e..7829a7625 100644 --- a/AUTHORING.md +++ b/AUTHORING.md @@ -1,6 +1,6 @@ # Authoring guidelines -These are the guidelines for writing documentation for Arcade, to help keep the tone and style consistent across the docs. +These are the guidelines for writing documentation for Arcade, to help keep the tone and style consistent across the docs. The first half covers writing; the second half covers how a page is put together in this repo (Fumadocs + MDX). ## Writing style @@ -26,3 +26,112 @@ These are the guidelines for writing documentation for Arcade, to help keep the - **Arcade**: Always refer to the product as "Arcade" in both titles and body text. Do not abbreviate or shorten the product name. Do not append "AI" to the product name. - **Engine**: Refer to "the Arcade Engine" when the deployment option is irrelevant. Otherwise, refer to "the Arcade Cloud Engine" or "a self-hosted Arcade Engine." - **MCP Servers**: MCP-Servers are collections of tools, either built by Arcade or a third party developer. For MCP Servers built by Arcade, refer to "the Arcade [Toolkit name] toolkit." For example, "the Arcade GitHub toolkit." + +## Where pages live + +All content is MDX under `content/docs/`. The file path is the URL: + +| File | URL | +| --- | --- | +| `content/docs/build/tools.mdx` | `/en/build/tools` | +| `content/docs/build/tools/index.mdx` | `/en/build/tools` (use this form when the page has child pages) | +| `content/docs/index.mdx` | `/en` | + +Toolkit (MCP server) reference pages at `/en/resources/integrations//` are not MDX files; they are generated from `toolkit-docs-generator/data/toolkits/*.json`. Edit their prose through `toolkit-docs-generator/curation/`. + +Internal links keep their current form (`/build/tools` or `/en/build/tools`); `proxy.ts` redirects locale-less paths. When you delete or move a page, add an entry to `redirects.ts` (`bun run check-redirects --auto-fix` drafts it for you). + +## Frontmatter + +Every page starts with a YAML frontmatter block. `title` is required; it is rendered as the page heading and used as the sidebar label, so **do not repeat it as a `# Heading` in the body**. + +```mdx +--- +title: Call a tool from an agent +description: Give an agent its first Arcade tool in five minutes. +--- +``` + +Optional fields: + +| Field | Purpose | +| --- | --- | +| `description` | Shown under the title and used for ``. Strongly recommended. | +| `metaTitle` | Overrides the `` tag when it should differ from the heading (for example, a longer SEO title). | +| `sidebarTitle` | Shorter label for the sidebar when `title` is long. | +| `full: true` | Full-width layout (no table of contents column). | +| `toc: false` | Hide the table of contents. | +| `breadcrumb: false` | Hide the breadcrumb. | +| `icon` | Lucide icon name for a top-level page (for example `Home`). | + +## Sidebar order: `meta.json` + +Each folder in `content/docs/` has a `meta.json` that controls its sidebar entry and the order of its children: + +```json +{ + "title": "Get Started", + "icon": "BookOpen", + "pages": [ + "index", + "quickstarts", + "---Frameworks---", + "agent-frameworks", + "[Arcade Dashboard](https://api.arcade.dev/dashboard)", + "..." + ] +} +``` + +- A bare entry (`"quickstarts"`) must match a sibling `quickstarts.mdx`, `quickstarts/index.mdx`, or `quickstarts/` folder. +- `"---Title---"` renders a separator; `"[Title](href)"` renders a link; `"..."` appends every unlisted sibling alphabetically; `"!slug"` hides a sibling. +- Unless the file contains `"..."`, every sibling page must be listed (`bun run check-meta` enforces both rules, and the pre-commit hook runs it on staged `meta.json` files). +- Page titles come from each page's frontmatter, and folder titles from the folder's own `meta.json` — not from the parent. +- `icon` names are [Lucide](https://lucide.dev/icons) icons; only top-level folders set one. +- The nine `content/docs/resources/integrations/<category>/meta.json` files are generated by `bun run sync-toolkit-sidebar`; do not edit them by hand. + +## Components + +These components are available in every MDX file without an import: `Callout`, `Tabs`/`Tabs.Tab`, `Steps`, `Cards`/`Cards.Card`, `GuideOverview` (with `.Outcomes`, `.Prerequisites`, `.YouWillLearn`), `SignupLink`, `DashboardLink`, `GlossaryTerm`, `ContactEmail`, `ToolCard`, `SampleAppCard`, `ToggleContent`, `SubpageList`, `ErrorHierarchy`, `ErrorCard`, `AgentFrameworkTabs`, `CheatSheetGrid`, `CheatSheetSection`, `InfoBox`, `MCPClientGrid`, and `Image`. + +```mdx +<Callout type="info">Use `type` of `default`, `info`, `warning`, `error`, or `important`.</Callout> + +<Tabs items={["Python", "JavaScript"]} storageKey="language"> + <Tabs.Tab>Python content</Tabs.Tab> + <Tabs.Tab>JavaScript content</Tabs.Tab> +</Tabs> + +<Steps> +### Install the SDK +... +### Call a tool +... +</Steps> +``` + +`Steps` numbers its `###` headings automatically. `SubpageList` lists the current folder's children: `<SubpageList basePath="/build/tools" />`. + +Page-specific components live in `components/pages/<section>/` and are imported with `@/components/pages/<section>/<name>`; shared components with `@/components/<name>`. + +## Code blocks + +Use fenced code blocks with a language. Fumadocs code-fence meta: + +````mdx +```python title="main.py" lineNumbers {3,5-7} +from arcadepy import Arcade +``` +```` + +- `title="path/to/file"` shows a filename header. +- `lineNumbers` shows line numbers (was `showLineNumbers`). +- `{1,3-5}` highlights lines (was `highlightLines={...}`). + +## Before you submit + +```bash +bun run vale:check # style rules +bun run check-meta # meta.json entries match files +bun run test # links, frontmatter, sitemap, and more +``` diff --git a/CLAUDE.md b/CLAUDE.md index 0bdfbf7b9..472788c9f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,65 +1,80 @@ # Arcade Docs -Arcade documentation site built with Next.js + Nextra (App Router), using pnpm as the package manager. Content is authored in MDX with custom React components. Check `package.json` for current framework versions. +Arcade documentation site built with Next.js (App Router, Turbopack) + Fumadocs, using **bun** as the package manager and script runner. Content is authored in MDX with custom React components. Check `package.json` for current framework versions. ## Commands ```bash -pnpm dev # Local dev server (port 3000) -pnpm build # Production build (next build --webpack) -pnpm lint # Lint with Ultracite (Biome-based) -pnpm format # Auto-format with Ultracite -pnpm test # Run all Vitest tests -pnpm test:watch # Watch mode -pnpm vale:check # Check docs against style rules +bun install # Install dependencies (pinned exact versions; bunfig enforces a 3-day minimum release age) +bun run dev # Local dev server (port 3000) +bun run build # Production build (next build, Turbopack) +bun run lint # Lint with Ultracite (Biome-based) +bun run format # Auto-format with Ultracite +bun run typecheck # tsc for the root, scripts/, and toolkit-docs-generator/ tsconfigs +bun run test # Run all Vitest tests +bun run test:watch # Watch mode +bun run vale:check # Check docs against style rules (vale content/docs/) +bun run check-meta # Validate meta.json "pages" entries against sibling files +bun run check-redirects # Validate redirects.ts and detect deleted pages without redirects +bun run sync-toolkit-sidebar # Regenerate integration category meta.json files from toolkit JSON ``` Run a single test: ```bash -pnpm vitest run tests/broken-link-check.test.ts +bunx vitest run tests/broken-link-check.test.ts ``` +Run a script directly with `bun scripts/<name>.ts` (never `tsx`, `npx`, `pnpm`, or `npm`). + ## Architecture -- **`app/en/`** — English docs content (MDX pages). Nextra file-based routing with `_meta.tsx` for navigation order. i18n handled via `middleware.ts`. -- **`app/_components/`** — Shared React components (tabbed code blocks, toolkit docs, callouts, etc.). -- **`app/_lib/`** — Data-fetching utilities (toolkit catalog, slug generation, static params). -- **`app/api/`** — API routes (markdown export, toolkit-data, glossary). -- **`toolkit-docs-generator/`** — Generates MCP toolkit documentation from server metadata JSON files in `toolkit-docs-generator/data/toolkits/`. -- **`scripts/`** — Build/CI scripts (Vale style fixes, redirect checking, llms.txt generation, Algolia crawler config, i18n sync). -- **`tests/`** — Vitest tests (broken links, internal link validation, sitemap, smoke tests). -- **`lib/`** — Next.js utilities (glossary remark plugin, llmstxt plugin). -- **`next.config.ts`** — Contains ~138 redirect rules. +- **`content/docs/`** — All MDX content. `content/docs/a/b.mdx` and `content/docs/a/b/index.mdx` both serve `/en/a/b`; `content/docs/index.mdx` serves `/en`. Each folder's `meta.json` (`{"title", "icon", "pages": [...]}`) sets sidebar order. Page tree is built by `lib/source.ts` (fumadocs `loader`, `baseUrl: "/en"`). +- **`app/en/[[...slug]]/page.tsx`** — Renders MDX pages from `lib/source.ts`; falls back to `lib/toolkit-route.tsx` for generated toolkit (MCP server) pages at `/en/resources/integrations/<category>/<toolkitId>`. +- **`components/mdx/`** — Nextra-compatible MDX components (`Callout`, `Tabs`, `Steps`, `Cards`, ...) registered globally in `mdx-components.tsx`; MDX files need no imports for them. +- **`components/pages/<section>/`** — Page-local components that used to sit beside a page; **`components/toolkit-docs/`** — toolkit reference page UI; other shared components live directly in `components/`. +- **`lib/`** — fumadocs source/layout config, toolkit data/slug/static-param helpers, integration catalog, glossary remark plugin, `remark-nextra-compat`, config and dictionaries. +- **`data/partner-toolkits.ts`** — Hand-authored partner integrations (no toolkit JSON). +- **`toolkit-docs-generator/`** — Generates MCP toolkit JSON into `toolkit-docs-generator/data/toolkits/`; `scripts/sync-toolkit-sidebar.ts` emits the nine integration category `meta.json` files. +- **`scripts/`** — Build/CI scripts (Vale style fixes/reviews, redirect checking, meta.json validation, internal link updates, llms.txt generation, Algolia crawler config sync). +- **`tests/`** — Vitest tests (broken links, internal links, frontmatter, sitemap, toolkit data, navigation contracts). `tests/helpers/content-paths.ts` maps content files ↔ URLs. +- **`redirects.ts`** — Redirect data module (~215 rules) imported by `next.config.ts`, the redirect checker, and tests. +- **`source.config.ts`** — fumadocs-mdx config (frontmatter schema, Shiki transformers). `proxy.ts` redirects locale-less and non-English paths to `/en`. ## Content Authoring -Follow **STYLEGUIDE.md** for writing standards and **AUTHORING.md** for formatting conventions. Key points: +Follow **STYLEGUIDE.md** for writing standards and **AUTHORING.md** for formatting conventions (frontmatter fields, `meta.json` format, code-fence meta). Key points: +- Every page needs frontmatter `title` (rendered as the page heading — do not repeat it as an `# H1` in the body) and should have `description`. - Sentence case for headings. Active voice. Direct tone. - Product is always "Arcade" (never abbreviated, never "Arcade AI"). - Use "Arcade Engine" (capitalized), "MCP server" (lowercase server), "tool" (lowercase), "auth provider". -- Code snippets: 4 spaces for Python, 2 spaces for other languages. -- Run `pnpm vale:check` before submitting docs changes. +- Code snippets: 4 spaces for Python, 2 spaces for other languages. Code-fence meta: `title="file.py"`, `lineNumbers`, `{1,3-5}`. +- New page: add the MDX file and list its slug in the folder's `meta.json` `pages` (unless the file contains `"..."`). Deleted or renamed page: add an entry to `redirects.ts`. +- Run `bun run vale:check` before submitting docs changes. Vale is a Go binary with no npm dependency, so a clean checkout does not have it. Install it once, then fetch the style packages (`styles/Google/`, `styles/alex/`, `styles/write-good/` are gitignored): ```bash brew install vale # or see https://vale.sh/docs/install -pnpm vale:sync +bun run vale:sync ``` ## Pre-commit Hooks -Husky runs on commit: Vale style checks on `.md/.mdx`, `_meta.tsx` key validation, redirect checking for deleted/renamed pages, internal link updates, and Ultracite formatting. You MUST fix any issues surfaced by the pre-commit hooks. NEVER bypass hooks with `--no-verify` or similar flags. +Husky runs on commit: Vale style checks on `.md/.mdx`, `meta.json` validation, redirect checking for deleted/renamed `content/docs` pages, internal link updates, and Ultracite formatting via lint-staged. You MUST fix any issues surfaced by the pre-commit hooks. NEVER bypass hooks with `--no-verify` or similar flags. + +## Dependencies + +Dependencies are pinned to exact versions and `bunfig.toml` rejects releases younger than three days. Add a package with `bun add --exact <pkg>@<version>` (or `bun add --exact --dev`), never by hand-editing `package.json`, and never use pnpm/npm/yarn. ## Key Config Files - `biome.jsonc` — Linter/formatter rules (Biome via Ultracite) - `.vale.ini` — Vale style checker config (Google style base + Arcade vocabulary) -- `.nvmrc` — Required Node version -- `tsconfig.json` — TypeScript compiler configuration -- `components.json` — shadcn/ui component config +- `bunfig.toml` — bun install policy (`minimumReleaseAge`) +- `tsconfig.json`, `scripts/tsconfig.json`, `toolkit-docs-generator/tsconfig.json` — TypeScript configs (`@/*` → repo root) +- `vitest.config.ts` — Test config (`@` alias; `tests/external-url-check.test.ts` runs only via `bun run test:external-urls`) - `postcss.config.mjs` — PostCSS/Tailwind config ## Code Quality diff --git a/Makefile b/Makefile index ee83ba55a..cf572ab02 100644 --- a/Makefile +++ b/Makefile @@ -7,19 +7,19 @@ help: install: ## Install the dependencies - @pnpm install + @bun install build: ## Build the docs site - @pnpm build + @bun run build lint: ## Lint the codebase - @pnpm run lint + @bun run lint test: ## Run the tests - @pnpm test -- --run + @bun run test run: ## Run the docs site locally - @pnpm dev + @bun run dev ruin: @echo "\033[31m\033[1m💀☠️💀☠️💀☠️💀☠️💀☠️💀☠️💀☠️💀☠️💀☠️💀☠️💀☠️💀☠️💀☠️💀☠️💀☠️\033[0m" diff --git a/README.md b/README.md index fe5b7ed54..a3fb79d7d 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,26 @@ # Arcade Documentation -Documentation for Arcade +Documentation for Arcade, built with [Next.js](https://nextjs.org/) and [Fumadocs](https://fumadocs.dev/). Content is MDX under `content/docs/`. ## Local Development -See the `package.json` for the dependencies required. +Requires [bun](https://bun.sh) (see `packageManager` in `package.json`). -First, run `pnpm install` to install the dependencies. +```bash +bun install # install dependencies +bun run dev # start the dev server at http://localhost:3000 +``` + +Other useful commands: -Then, run `pnpm dev` to start the development server and visit localhost:3000. +```bash +bun run build # production build +bun run lint # Ultracite (Biome) lint +bun run typecheck # TypeScript across root, scripts/, toolkit-docs-generator/ +bun run test # Vitest +bun run check-meta # validate meta.json sidebar entries +bun run check-redirects +``` ### Environment Variables @@ -23,6 +35,14 @@ cp .env.local.example .env.local | `OPENAI_API_KEY` | Yes | Generates llms.txt with AI-powered summaries | | `ANTHROPIC_API_KEY` | No | Enables AI-powered style fixes (local and CI) | +## Writing docs + +- Pages are `content/docs/**/*.mdx`; `content/docs/a/b.mdx` and `content/docs/a/b/index.mdx` both serve `/en/a/b`. +- Each folder's `meta.json` sets the sidebar title, icon, and `pages` order. +- Every page needs frontmatter `title` (rendered as the heading) and should have `description`. + +See [AUTHORING.md](./AUTHORING.md) for frontmatter fields, `meta.json` format, components, and code-fence options, and [STYLEGUIDE.md](./STYLEGUIDE.md) for voice and tone. + ## Style Guide & Linting We use [Vale](https://vale.sh/) to enforce documentation style standards based on the Google Developer Documentation Style Guide. @@ -39,12 +59,13 @@ Both are powered by Vale + LLM and require `ANTHROPIC_API_KEY` (or `OPENAI_API_K ### Local Development -First, [install the Vale CLI](https://vale.sh/docs/install) for your platform. +First, [install the Vale CLI](https://vale.sh/docs/install) for your platform, then fetch the style packages: ```bash -pnpm vale:check # Check all docs for style issues -pnpm vale app/en/path/ # Check specific path -pnpm vale:fix <file> # Interactive AI-powered fixes (optional) +bun run vale:sync +bun run vale:check # Check all docs for style issues +bun run vale content/docs/build/ # Check a specific path +bun run vale:fix <file> # Interactive AI-powered fixes (optional) ``` The `vale:fix` command requires an API key in `.env.local`. Without one, you can fix issues manually using `vale <file>` to see detailed output. @@ -52,21 +73,16 @@ The `vale:fix` command requires an API key in `.env.local`. Without one, you can ### Style resources - [STYLEGUIDE.md](./STYLEGUIDE.md) - Writing standards for voice, tone, and structure +- [AUTHORING.md](./AUTHORING.md) - Page structure, frontmatter, meta.json, components - [AGENTS.md](./AGENTS.md) - Instructions for AI assistants working on docs ## llms.txt Generation -The project includes a Next.js plugin that automatically generates an `llms.txt` file following the [llms.txt specification](https://llmstxt.org/). This file helps LLMs understand and navigate the documentation. - -**Automatic generation**: Runs during production builds (`pnpm build`) - -**Manual generation**: Run `pnpm llmstxt` to regenerate the file - -See [`scripts/README-llmstxt.md`](./scripts/README-llmstxt.md) for detailed documentation. +`scripts/generate-llmstxt.ts` produces `public/llms.txt` following the [llms.txt specification](https://llmstxt.org/) from every page under `content/docs` plus the generated toolkit pages. It runs in CI (`.github/workflows/llmstxt.yml`) when content changes; run `bun run llmstxt` to regenerate it locally (needs `OPENAI_API_KEY`). -## Styling +## Callouts -We use Nextra's built-in Callout component for callouts in MDX: +Use the `Callout` component (available in every MDX file without an import): ```mdx <Callout>A general callout. Use sparingly.</Callout> @@ -82,8 +98,6 @@ We use Nextra's built-in Callout component for callouts in MDX: </Callout> ``` -## Generate documentation for a MCP Server toolkit +## Toolkit (MCP server) pages -Run `make toolkit-docs` and follow the instructions in the terminal. This command -will automatically generate documentation for the tools in a specified Arcade MCP -Server Python package. +Toolkit reference pages at `/en/resources/integrations/<category>/<toolkit>` are generated from `toolkit-docs-generator/data/toolkits/*.json`, not from MDX. See [`toolkit-docs-generator/README.md`](./toolkit-docs-generator/README.md) for the generator and [`toolkit-docs-generator/CURATION.md`](./toolkit-docs-generator/CURATION.md) for hand-authored prose. Sidebar entries for them are produced by `bun run sync-toolkit-sidebar`. diff --git a/algolia/README.md b/algolia/README.md index 806f547e2..7b73fd49d 100644 --- a/algolia/README.md +++ b/algolia/README.md @@ -23,7 +23,7 @@ Two GitHub Actions workflows keep the index fresh. They never overlap. | When | Workflow | What happens | | --- | --- | --- | -| Docs content is published (production deploy) | `algolia-reindex.yml` | Triggers a reindex, but only when the deploy touched `app/en/**` or `toolkit-docs-generator/data/toolkits/**` | +| Docs content is published (production deploy) | `algolia-reindex.yml` | Triggers a reindex, but only when the deploy touched `content/docs/**` or `toolkit-docs-generator/data/toolkits/**` | | `crawler-config.js` changes on `main` | `sync-crawler-config.yml` | Pushes the config to the Crawler API, then triggers a reindex | The crawler also runs on a weekly schedule as a backstop. @@ -37,7 +37,7 @@ The crawler also runs on a weekly schedule as a backstop. 3. Open a PR. On merge, `sync-crawler-config.yml` applies it and reindexes. To apply the config without a merge, run the sync workflow manually from the -Actions tab (`workflow_dispatch`), or run `pnpm sync-crawler-config` locally with +Actions tab (`workflow_dispatch`), or run `bun run sync-crawler-config` locally with the crawler credentials set as environment variables. ## Configuration and secrets @@ -66,7 +66,7 @@ using this key. Without the key the config still syncs and a reindex still runs, but the index-settings step is skipped with a warning. The public search credentials used by the frontend widget -(`app/_components/algolia-search.tsx`) are separate `NEXT_PUBLIC_ALGOLIA_*` +(`components/algolia-search.tsx`) are separate `NEXT_PUBLIC_ALGOLIA_*` environment variables. ## Troubleshooting diff --git a/algolia/crawler-config.js b/algolia/crawler-config.js index d7bc2005e..ed1a6b98c 100644 --- a/algolia/crawler-config.js +++ b/algolia/crawler-config.js @@ -7,7 +7,7 @@ * * To apply a change to the live crawler, edit this file — merging it to main * runs `.github/workflows/sync-crawler-config.yml`, which pushes it via the - * Crawler API and triggers a reindex (`pnpm sync-crawler-config`). To apply it + * Crawler API and triggers a reindex (`bun run sync-crawler-config`). To apply it * by hand instead, wrap the object below in `new Crawler({ ...crawlerConfig, * apiKey })` in the dashboard editor and run a crawl. * diff --git a/app/_components/copy-page-override.tsx b/app/_components/copy-page-override.tsx deleted file mode 100644 index 611d679db..000000000 --- a/app/_components/copy-page-override.tsx +++ /dev/null @@ -1,208 +0,0 @@ -"use client"; - -import { usePathname } from "next/navigation"; -import { useCallback, useEffect, useRef } from "react"; - -const COPY_FEEDBACK_DELAY_MS = 2000; -const COPY_BUTTON_TEXT = "Copy page"; -const COPIED_TEXT = "Copied"; -const COPYING_TEXT = "Copying\u2026"; -const COPY_FAILED_TEXT = "Failed to copy"; -const DROPDOWN_IDENTIFIER = "Markdown for LLMs"; - -// A toolkit reference page: /<locale>/resources/integrations/<category>/<slug>. -// Captures the slug so we can pull full markdown from the data route. -const TOOLKIT_PAGE_PATH = - /^\/[^/]+\/resources\/integrations\/[^/]+\/([^/]+)\/?$/; - -const ICON_COPY = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16"><rect x="9" y="9" width="13" height="13" rx="2"></rect><path d="M5 15H4C2.89543 15 2 14.1046 2 13V4C2 2.89543 2.89543 2 4 2H13C14.1046 2 15 2.89543 15 4V5"></path></svg>`; - -const ICON_SPINNER = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" class="x:animate-spin"><circle cx="12" cy="12" r="10" stroke-opacity="0.25"></circle><path d="M12 2a10 10 0 0 1 10 10" stroke-linecap="round"></path></svg>`; - -const ICON_CHECK = `<svg viewBox="0 0 24 24" fill="none" stroke="var(--color-green-600)" stroke-width="2.5" width="16"><path d="M20 6L9 17l-5-5" stroke-linecap="round" stroke-linejoin="round"></path></svg>`; - -const ICON_ERROR = `<svg viewBox="0 0 24 24" fill="none" stroke="var(--color-red-600)" stroke-width="2" width="16"><circle cx="12" cy="12" r="10"></circle><line x1="15" y1="9" x2="9" y2="15" stroke-linecap="round"></line><line x1="9" y1="9" x2="15" y2="15" stroke-linecap="round"></line></svg>`; - -function findButtonTextNode(button: HTMLButtonElement): Text | null { - for (const node of button.childNodes) { - if (node.nodeType === Node.TEXT_NODE && node.textContent?.trim()) { - return node as Text; - } - } - return null; -} - -function setButtonIcon(button: HTMLButtonElement, iconHTML: string): void { - const svg = button.querySelector("svg"); - if (svg) { - svg.outerHTML = iconHTML; - } -} - -function getCopyButton(): HTMLButtonElement | null { - for (const button of document.querySelectorAll("button")) { - const text = button.textContent || ""; - if ( - text.includes(COPY_BUTTON_TEXT) || - text.includes(COPIED_TEXT) || - text.includes(COPYING_TEXT) || - text.includes(COPY_FAILED_TEXT) - ) { - return button; - } - } - return null; -} - -/** - * This component overrides the default nextra-theme-docs "Copy page" button behavior - * to fetch markdown from our API instead of copying raw MDX source. - */ -export function CopyPageOverride() { - const pathname = usePathname(); - const isFetchingRef = useRef(false); - const feedbackTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined); - - const setButtonState = useCallback( - (button: HTMLButtonElement, text: string) => { - const textNode = findButtonTextNode(button); - if (textNode) { - textNode.textContent = text; - } - - if (text === COPYING_TEXT) { - setButtonIcon(button, ICON_SPINNER); - button.style.color = ""; - } else if (text === COPIED_TEXT) { - setButtonIcon(button, ICON_CHECK); - button.style.color = "var(--color-green-600)"; - } else if (text === COPY_FAILED_TEXT) { - setButtonIcon(button, ICON_ERROR); - button.style.color = "var(--color-red-600)"; - } else { - setButtonIcon(button, ICON_COPY); - button.style.color = ""; - } - }, - [] - ); - - const fetchAndCopyMarkdown = useCallback(async (): Promise<boolean> => { - try { - let markdown: string | null = null; - - // Toolkit reference pages render per-tool detail client-only, so the edge - // HTML→markdown view would miss parameters/output/examples. Pull full - // markdown from the data route instead. If the slug isn't a generated - // toolkit (e.g. a static partner page) the route 404s and we fall back to - // the normal page fetch below. - const toolkitSlug = pathname.match(TOOLKIT_PAGE_PATH)?.[1]; - if (toolkitSlug) { - const dataResponse = await fetch( - `/api/toolkit-data/${encodeURIComponent(toolkitSlug)}`, - { headers: { Accept: "text/markdown" } } - ); - if (dataResponse.ok) { - markdown = await dataResponse.text(); - } - } - - if (markdown === null) { - const response = await fetch(pathname, { - headers: { Accept: "text/markdown" }, - }); - if (!response.ok) { - throw new Error(`Failed to fetch markdown: ${response.status}`); - } - markdown = await response.text(); - } - - await navigator.clipboard.writeText(markdown); - return true; - } catch { - return false; - } - }, [pathname]); - - const handleCopy = useCallback( - async (button: HTMLButtonElement) => { - if (isFetchingRef.current) { - return; - } - - clearTimeout(feedbackTimerRef.current); - isFetchingRef.current = true; - setButtonState(button, COPYING_TEXT); - - const success = await fetchAndCopyMarkdown(); - - isFetchingRef.current = false; - setButtonState(button, success ? COPIED_TEXT : COPY_FAILED_TEXT); - - feedbackTimerRef.current = setTimeout(() => { - setButtonState(button, COPY_BUTTON_TEXT); - }, COPY_FEEDBACK_DELAY_MS); - }, - [fetchAndCopyMarkdown, setButtonState] - ); - - useEffect(() => { - const isCopyButton = (button: HTMLButtonElement): boolean => { - const text = button.textContent || ""; - return ( - text.includes(COPY_BUTTON_TEXT) || - text.includes(COPIED_TEXT) || - text.includes(COPYING_TEXT) || - text.includes(COPY_FAILED_TEXT) - ); - }; - - const handleButtonClick = async (event: MouseEvent): Promise<void> => { - const target = event.target as HTMLElement; - const button = target.closest("button") as HTMLButtonElement | null; - - if (!(button && isCopyButton(button))) { - return; - } - - event.preventDefault(); - event.stopPropagation(); - - await handleCopy(button); - }; - - const handleDropdownClick = async (event: MouseEvent): Promise<void> => { - const target = event.target as HTMLElement; - const option = target.closest('[role="option"]'); - const optionText = option?.textContent || ""; - - const isDropdownCopyOption = - optionText.includes(COPY_BUTTON_TEXT) && - optionText.includes(DROPDOWN_IDENTIFIER); - - if (!isDropdownCopyOption) { - return; - } - - event.preventDefault(); - event.stopPropagation(); - - document.body.click(); - - const button = getCopyButton(); - if (button) { - await handleCopy(button); - } - }; - - document.addEventListener("click", handleButtonClick, true); - document.addEventListener("click", handleDropdownClick, true); - - return () => { - document.removeEventListener("click", handleButtonClick, true); - document.removeEventListener("click", handleDropdownClick, true); - }; - }, [handleCopy]); - - return null; -} diff --git a/app/_components/custom-layout.tsx b/app/_components/custom-layout.tsx deleted file mode 100644 index 1e048ff46..000000000 --- a/app/_components/custom-layout.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import type React from "react"; -import { CopyPageOverride } from "@/app/_components/copy-page-override"; -import { PlaceholderReplacer } from "@/app/_components/placeholder-replacer"; -import { OrySessionProvider } from "@/app/_lib/ory-session-context"; - -const CustomLayout: React.FC<{ children: React.ReactNode }> = ({ - children, -}) => ( - <OrySessionProvider> - <PlaceholderReplacer /> - <CopyPageOverride /> - <main className="custom-main">{children}</main> - </OrySessionProvider> -); - -export default CustomLayout; diff --git a/app/_components/custom-pre.tsx b/app/_components/custom-pre.tsx deleted file mode 100644 index 40c08e1db..000000000 --- a/app/_components/custom-pre.tsx +++ /dev/null @@ -1,135 +0,0 @@ -"use client"; - -import { FileText, Terminal } from "lucide-react"; -import { Pre } from "nextra/components"; -import type React from "react"; - -type CustomPreProps = { - children?: React.ReactNode; - className?: string; - "data-language"?: string; - [key: string]: unknown; -}; - -const languageDisplayNames: Record<string, string> = { - js: "JavaScript", - javascript: "JavaScript", - ts: "TypeScript", - typescript: "TypeScript", - py: "Python", - python: "Python", - java: "Java", - cpp: "C++", - c: "C", - go: "Go", - rust: "Rust", - php: "PHP", - rb: "Ruby", - ruby: "Ruby", - sql: "SQL", - json: "JSON", - yaml: "YAML", - yml: "YAML", - xml: "XML", - html: "HTML", - css: "CSS", - scss: "SCSS", - sass: "Sass", - jsx: "JSX", - tsx: "TSX", - md: "Markdown", - markdown: "Markdown", - toml: "TOML", -}; - -const getLanguageDisplayName = (lang: string): string => - languageDisplayNames[lang.toLowerCase()] || lang.toUpperCase(); - -const CustomPre: React.FC<CustomPreProps> = ({ - children, - className, - "data-language": dataLanguage, - ...props -}) => { - // Parse language (remove filename support for simplicity) - const language = dataLanguage || className?.replace("language-", "") || ""; - - const isTerminalLanguage = [ - "bash", - "shell", - "sh", - "zsh", - "fish", - "powershell", - "ps1", - "cmd", - ].includes(language.toLowerCase()); - - // If we have a language, add custom wrapper with header - if (language && language.trim() !== "") { - if (isTerminalLanguage) { - // Terminal styling - return ( - <div className="my-4 overflow-hidden rounded-lg border border-gray-300 bg-gray-100 dark:border-gray-700 dark:bg-gray-900"> - {/* Terminal Header */} - <div className="flex items-center justify-between bg-gray-200 px-4 py-2 text-gray-700 dark:bg-gray-800 dark:text-gray-300"> - <div className="flex items-center gap-2"> - <Terminal className="h-4 w-4" /> - <span className="font-medium text-sm">Terminal</span> - </div> - <div className="flex gap-2"> - <div className="h-3 w-3 rounded-full bg-red-500" /> - <div className="h-3 w-3 rounded-full bg-yellow-500" /> - <div className="h-3 w-3 rounded-full bg-green-500" /> - </div> - </div> - - {/* Code content with syntax highlighting preserved */} - <div className="overflow-x-auto"> - <Pre - className={`text-gray-800 text-sm dark:text-gray-100 ${className || ""}`} - style={{ margin: 0, borderRadius: 0, background: "transparent" }} - {...props} - > - {children} - </Pre> - </div> - </div> - ); - } - // Code block styling - return ( - <div className="my-4 overflow-hidden rounded border border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-950"> - {/* Code Header */} - <div className="flex items-center justify-between border-gray-200 border-b bg-gray-50 px-4 py-2 dark:border-gray-800 dark:bg-gray-900"> - <div className="flex items-center gap-2"> - <FileText className="h-4 w-4 text-gray-600 dark:text-gray-400" /> - <span className="font-medium text-gray-700 text-sm dark:text-gray-300"> - {getLanguageDisplayName(language)} - </span> - </div> - </div> - - {/* Code content with syntax highlighting preserved */} - <div className="overflow-x-auto"> - <Pre - className={`bg-white text-sm dark:bg-gray-950 ${className || ""}`} - style={{ margin: 0, borderRadius: 0 }} - {...props} - > - {children} - </Pre> - </div> - </div> - ); - } - - // For code blocks without language, use default pre component - return ( - <Pre className={className} data-language={dataLanguage} {...props}> - {children} - </Pre> - ); -}; - -export default CustomPre; diff --git a/app/_components/dynamic-survey/multiple-choice-question.tsx b/app/_components/dynamic-survey/multiple-choice-question.tsx deleted file mode 100644 index 12c821dbb..000000000 --- a/app/_components/dynamic-survey/multiple-choice-question.tsx +++ /dev/null @@ -1,102 +0,0 @@ -"use client"; -import { Checkbox, Input, Label } from "@arcadeai/design-system"; -import type { MultipleSurveyQuestion } from "posthog-js"; -import { useMemo, useState } from "react"; - -type MultipleChoiceQuestionProps = { - question: MultipleSurveyQuestion; - previousResponse?: string[]; -}; - -export function MultipleChoiceQuestion({ - question, - previousResponse, -}: MultipleChoiceQuestionProps) { - const hasOtherInChoices = question.choices?.some( - (choice) => choice.toLowerCase() === "other" - ); - - const [selectedChoices, setSelectedChoices] = useState<string[]>( - previousResponse || [] - ); - - const shuffledChoices = useMemo(() => { - if (!(question.choices?.length && question.shuffleOptions)) { - return question.choices; - } - - const [others, regular] = question.choices.reduce<[string[], string[]]>( - ([o, r], choice) => - choice.toLowerCase() === "other" - ? [[...o, choice], r] - : [o, [...r, choice]], - [[], []] - ); - - const ShuffleRandomOffset = 0.5; - return [ - ...regular.sort(() => Math.random() - ShuffleRandomOffset), - ...others, - ]; - }, [question.choices, question.shuffleOptions]); - - const handleCheckboxChange = (choice: string, checked: boolean) => { - setSelectedChoices((prev) => { - if (checked) { - return [...prev, choice]; - } - return prev.filter((c) => c !== choice); - }); - }; - - const hasOtherSelected = hasOtherInChoices - ? selectedChoices.some((choice) => choice.toLowerCase() === "other") - : selectedChoices.includes("other"); - - return ( - <div className="space-y-3"> - {shuffledChoices?.map((choice) => ( - <div className="flex items-center space-x-2" key={choice}> - <Checkbox - defaultChecked={previousResponse?.includes(choice)} - id={choice} - name={choice} - onCheckedChange={(checked) => - handleCheckboxChange(choice, checked as boolean) - } - required={!question.optional && selectedChoices.length === 0} - /> - <Label htmlFor={choice}>{choice}</Label> - </div> - ))} - {question.hasOpenChoice && !hasOtherInChoices && ( - <div className="flex items-center space-x-2"> - <Checkbox - checked={hasOtherSelected} - id="other" - onCheckedChange={(checked) => - handleCheckboxChange("other", checked as boolean) - } - /> - <Label htmlFor="other">Other</Label> - </div> - )} - {question.hasOpenChoice && hasOtherSelected && ( - <div className="mt-4"> - <Input - className="mt-1" - defaultValue={ - previousResponse?.find( - (choice) => !question.choices?.includes(choice) - ) || "" - } - id="other" - name="other" - placeholder="Please specify" - required={!question.optional} - /> - </div> - )} - </div> - ); -} diff --git a/app/_components/subpage-list.tsx b/app/_components/subpage-list.tsx deleted file mode 100644 index 96cf817e2..000000000 --- a/app/_components/subpage-list.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import Link from "next/link"; - -type SubpageListProps = { - basePath: string; - meta: Record<string, string | { title: string } | unknown>; -}; - -export function SubpageList({ basePath, meta }: SubpageListProps) { - const subpages = Object.entries(meta).filter( - ([key]) => key !== "index" && key !== "*" - ); - - return ( - <ul className="x:ms-[1.5em] x:not-first:mt-[1.25em] x:list-disc x:[:is(ol,ul)_&]:my-[.75em]"> - {subpages.map(([key, title]) => { - let linkText: string; - if (typeof title === "string") { - linkText = title; - } else if ( - typeof title === "object" && - title !== null && - "title" in title - ) { - linkText = (title as { title: string }).title; - } else { - linkText = String(title); - } - return ( - <li className="x:my-[.5em]" key={key}> - <Link - className="x:focus-visible:nextra-focus x:text-primary-600 x:underline x:decoration-from-font x:[text-underline-position:from-font] x:hover:no-underline" - href={`${basePath}/${key}`} - > - {linkText} - </Link> - </li> - ); - })} - </ul> - ); -} diff --git a/app/_components/tabbed-code-block/language-tabs.tsx b/app/_components/tabbed-code-block/language-tabs.tsx deleted file mode 100644 index a21831fe6..000000000 --- a/app/_components/tabbed-code-block/language-tabs.tsx +++ /dev/null @@ -1,41 +0,0 @@ -"use client"; -import { Button } from "@arcadeai/design-system"; -import { cn } from "@arcadeai/design-system/lib/utils"; - -export function LanguageTabs({ - languages, - currentLanguage, - onSelect, -}: { - languages: string[]; - currentLanguage: string; - onSelect: (language: string) => void; -}) { - return ( - <div - aria-label="Programming languages" - className="inline-flex items-center gap-1.5" - role="tablist" - > - {languages.map((lang) => ( - <Button - aria-label={`Switch to ${lang} example`} - aria-selected={currentLanguage === lang} - className={cn( - "h-9 px-4 font-medium text-sm focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background", - currentLanguage === lang - ? "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90 hover:text-primary-foreground active:bg-primary/80 active:text-primary-foreground" - : "text-muted-foreground hover:bg-primary/60 hover:text-foreground active:bg-primary/60 active:text-foreground" - )} - key={lang} - onClick={() => onSelect(lang)} - role="tab" - size="sm" - variant="secondary" - > - {lang} - </Button> - ))} - </div> - ); -} diff --git a/app/api/toolkit-data/[toolkitId]/route.ts b/app/api/toolkit-data/[toolkitId]/route.ts index 42f1bb912..5ea01ed97 100644 --- a/app/api/toolkit-data/[toolkitId]/route.ts +++ b/app/api/toolkit-data/[toolkitId]/route.ts @@ -1,6 +1,6 @@ import { NextResponse } from "next/server"; -import { readToolkitData } from "@/app/_lib/toolkit-data"; -import { toToolkitMarkdown } from "@/app/_lib/toolkit-markdown"; +import { readToolkitData } from "@/lib/toolkit-data"; +import { toToolkitMarkdown } from "@/lib/toolkit-markdown"; // Cache headers for toolkit data responses. This route content-negotiates on // Accept (JSON vs. text/markdown), so Vary: Accept is required — without it a diff --git a/app/en/[[...slug]]/page.tsx b/app/en/[[...slug]]/page.tsx new file mode 100644 index 000000000..5531571d3 --- /dev/null +++ b/app/en/[[...slug]]/page.tsx @@ -0,0 +1,232 @@ +import type { MDXComponents } from "mdx/types"; +import type { Metadata } from "next"; +import { notFound } from "next/navigation"; +import { Children, cloneElement, isValidElement, type ReactNode } from "react"; +import { DocsPageShell } from "@/components/chrome/page"; +import type { TocEntry } from "@/components/chrome/stores"; +import { pageLastModified, toolkitPageLastModified } from "@/lib/last-modified"; +import { DOCS_REPOSITORY_BASE } from "@/lib/layout.shared"; +import { smartypants } from "@/lib/remark-smartypants"; +import { source } from "@/lib/source"; +import { + generateToolkitParams, + matchToolkitRoute, + ToolkitRoutePage, + toolkitMarkdownUrl, + toolkitRouteMetadata, +} from "@/lib/toolkit-route"; +import { getMDXComponents } from "@/mdx-components"; + +type Props = { + params: Promise<{ slug?: string[] }>; +}; + +/** + * Pages that open with a second-level heading and show no page title. Their + * frontmatter `title` only feeds `<title>` and the sidebar. + */ +const PAGES_WITHOUT_TITLE = new Set([ + "get-started/agent-frameworks/langchain/auth-langchain-tools", + "get-started/agent-frameworks/mastra", + "resources/examples", +]); + +function githubEditUrl(filePath: string): string { + return `${DOCS_REPOSITORY_BASE}/content/docs/${filePath}`; +} + +function markdownUrl(slug: string[] | undefined): string { + return `/llms.mdx/${(slug ?? []).join("/")}`; +} + +function lastModifiedDate(data: unknown): string | null { + const value = (data as { lastModified?: unknown }).lastModified; + if (value instanceof Date) { + return value.toISOString(); + } + if (typeof value === "string" || typeof value === "number") { + const date = new Date(value); + return Number.isNaN(date.getTime()) ? null : date.toISOString(); + } + return null; +} + +const INLINE_CODE = /`([^`]+)`/; + +/** + * Renders a frontmatter title the way the MDX body renders it: `code` spans + * as code, and typographic punctuation in the text around them. + */ +function inlineCode( + title: string, + Code: MDXComponents["code"] | undefined +): ReactNode { + const parts = title.split(INLINE_CODE); + if (parts.length === 1 || typeof Code !== "function") { + return smartypants(title); + } + // Odd parts are the text between backticks. + let offset = 0; + let isCode = false; + const nodes: ReactNode[] = []; + for (const part of parts) { + nodes.push( + isCode ? <Code key={`code-${offset}`}>{part}</Code> : smartypants(part) + ); + offset += part.length + 1; + isCode = !isCode; + } + return nodes; +} + +/** + * Table-of-contents titles come from the headings as plain elements. Inline + * code in them is rendered with the content's own `code` component, so an + * entry shows the same boxed code as the heading it points to. + */ +function tocTitle(node: ReactNode, Code: MDXComponents["code"]): ReactNode { + if (typeof Code !== "function") { + return node; + } + return Children.map(node, (child) => { + if (!isValidElement<{ children?: ReactNode }>(child)) { + return child; + } + if (child.type === "code") { + return <Code>{child.props.children}</Code>; + } + // Fragments and other inline wrappers: look inside them. + return child.props.children === undefined + ? child + : cloneElement(child, undefined, tocTitle(child.props.children, Code)); + }); +} + +/** + * Children of the element returned by `ToolkitRoutePage`. Its own page + * wrapper is replaced by the docs shell. + */ +function innerChildren(node: ReactNode): ReactNode { + if (isValidElement<{ children?: ReactNode }>(node)) { + return node.props.children; + } + return node; +} + +// Every route is known at build time (MDX pages + generated toolkit pages); +// unknown paths get the static 404 without running page code. +export const dynamicParams = false; + +export default async function Page(props: Props) { + const params = await props.params; + const page = source.getPage(params.slug); + + if (!page) { + const match = matchToolkitRoute(params.slug); + if (match) { + const toolkitPage = await ToolkitRoutePage({ match }); + if (toolkitPage) { + // Toolkit pages list their tools in their own panel: the shell keeps + // the table of contents column but has no headings to show in it. + // The sidebar breadcrumb stays above the toolkit's own breadcrumb, as + // on production. + return ( + <DocsPageShell + breadcrumb + lastModified={toolkitPageLastModified()} + markdownUrl={toolkitMarkdownUrl(match)} + > + {innerChildren(toolkitPage)} + </DocsPageShell> + ); + } + } + notFound(); + } + + const { body: MDX, full, breadcrumb, landing, title } = page.data; + const components = getMDXComponents(); + const Wrapper = components.wrapper; + const Heading = components.h1; + // A `toc: false` frontmatter value replaces the generated heading list. + const headings: unknown = page.data.toc; + const showToc = headings !== false && !landing; + const showTitle = + !(landing || PAGES_WITHOUT_TITLE.has(page.slugs.join("/"))) && + typeof Heading === "function"; + + const tocItems: TocEntry[] = Array.isArray(headings) + ? (headings as typeof page.data.toc).map((item) => ({ + depth: item.depth, + id: item.url.slice(1), + title: tocTitle(item.title, components.code), + })) + : []; + + // The page title is the first element of the content, so the spacing rules + // between it and what follows are the content's own. + const content = (children: ReactNode) => ( + <> + {showTitle ? ( + <Heading>{inlineCode(title, components.code)}</Heading> + ) : null} + {children} + </> + ); + + return ( + <DocsPageShell + breadcrumb={breadcrumb !== false && !landing} + editUrl={githubEditUrl(page.path)} + lastModified={pageLastModified(page.path, lastModifiedDate(page.data))} + layout={full || landing ? "full" : "default"} + markdownUrl={showToc ? markdownUrl(params.slug) : undefined} + toc={showToc} + tocItems={tocItems} + > + <MDX + components={{ + ...components, + wrapper: ({ children }: { children?: ReactNode }) => + typeof Wrapper === "function" ? ( + <Wrapper>{content(children)}</Wrapper> + ) : ( + content(children) + ), + }} + /> + </DocsPageShell> + ); +} + +export async function generateStaticParams() { + const toolkitParams = await generateToolkitParams(); + return [...source.generateParams(), ...toolkitParams]; +} + +export async function generateMetadata(props: Props): Promise<Metadata> { + const params = await props.params; + const page = source.getPage(params.slug); + + if (!page) { + const match = matchToolkitRoute(params.slug); + if (match) { + const metadata = await toolkitRouteMetadata(match); + if (metadata) { + return metadata; + } + } + notFound(); + } + + // Open Graph and Twitter tags are derived from the title and description; + // pages without a description fall back to the site description + // (app/layout.tsx). + const metadata: Metadata = { + title: page.data.metaTitle ?? page.data.title, + }; + if (page.data.description) { + metadata.description = page.data.description; + } + return metadata; +} diff --git a/app/en/_meta.tsx b/app/en/_meta.tsx deleted file mode 100644 index d12fbd051..000000000 --- a/app/en/_meta.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { - Book, - BookOpen, - GraduationCap, - Home, - Pencil, - Ruler, - ServerCog, -} from "lucide-react"; -import type { MetaRecord } from "nextra"; - -function TitleWithIcon({ - icon: Icon, - children, -}: { - icon: React.ComponentType<{ className?: string }>; - children: React.ReactNode; -}) { - return ( - <span className="flex items-center gap-2 font-medium text-gray-950 dark:text-white"> - <Icon className="size-4" /> - {children} - </span> - ); -} - -export const meta: MetaRecord = { - "*": { - theme: { - copyPage: true, - }, - }, - home: { - title: <TitleWithIcon icon={Home}>Docs Home</TitleWithIcon>, - theme: { - breadcrumb: false, - layout: "full", - toc: false, - copyPage: false, - }, - }, - "get-started": { - title: <TitleWithIcon icon={BookOpen}>Get Started</TitleWithIcon>, - }, - build: { - title: <TitleWithIcon icon={Ruler}>Build</TitleWithIcon>, - }, - operate: { - title: <TitleWithIcon icon={ServerCog}>Operate</TitleWithIcon>, - }, - resources: { - title: <TitleWithIcon icon={Pencil}>Resources</TitleWithIcon>, - }, - learn: { - title: <TitleWithIcon icon={Book}>Learn</TitleWithIcon>, - }, - references: { - title: <TitleWithIcon icon={GraduationCap}>References</TitleWithIcon>, - }, -}; - -export default meta; diff --git a/app/en/build/_meta.tsx b/app/en/build/_meta.tsx deleted file mode 100644 index 1ac006542..000000000 --- a/app/en/build/_meta.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import type { MetaRecord } from "nextra"; - -export const meta: MetaRecord = { - index: { - title: "Overview", - }, - "quickstart-call-tools": { - title: "Quickstart: call tools in agents", - href: "/get-started/quickstarts/call-tool-agent", - }, - "quickstart-mcp-server": { - title: "Quickstart: build an MCP server", - href: "/get-started/quickstarts/mcp-server-quickstart", - }, - "tool-calling": { - title: "Call tools", - }, - "create-tools": { - title: "Create tools", - }, - "user-facing-agents": { - title: "User-facing agents", - }, - "arcade-deploy": { - title: "Arcade Deploy", - }, -}; - -export default meta; diff --git a/app/en/build/create-tools/_meta.tsx b/app/en/build/create-tools/_meta.tsx deleted file mode 100644 index 89d3d35cb..000000000 --- a/app/en/build/create-tools/_meta.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import type { MetaRecord } from "nextra"; - -export const meta: MetaRecord = { - "tool-basics": { - title: "Build a tool", - href: "/build/create-tools/tool-basics", - }, - "evaluate-tools": { - title: "Evaluate tools", - href: "/build/create-tools/evaluate-tools", - }, - improve: { - title: "Improve an existing toolkit", - }, - "error-handling": { - title: "Handle errors", - href: "/build/create-tools/error-handling", - }, - "secure-your-server": { - title: "Secure your server", - }, - "migrate-toolkits": { - title: "Migrate from toolkits to MCP servers", - }, -}; - -export default meta; diff --git a/app/en/build/create-tools/error-handling/_meta.tsx b/app/en/build/create-tools/error-handling/_meta.tsx deleted file mode 100644 index a67ab3a0f..000000000 --- a/app/en/build/create-tools/error-handling/_meta.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "*": { - theme: { - breadcrumb: true, - toc: true, - copyPage: true, - }, - }, - index: { - title: "Overview", - }, - "retry-tools": { - title: "Retry Tools with Improved Prompt", - }, - "useful-tool-errors": { - title: "Provide Useful Tool Errors", - }, -}; - -export default meta; diff --git a/app/en/build/create-tools/evaluate-tools/_meta.tsx b/app/en/build/create-tools/evaluate-tools/_meta.tsx deleted file mode 100644 index ddcb80e01..000000000 --- a/app/en/build/create-tools/evaluate-tools/_meta.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "*": { - theme: { - breadcrumb: true, - toc: true, - copyPage: true, - }, - }, - index: { - title: "Overview", - }, - "why-evaluate": { - title: "Why evaluate tools?", - }, - "create-evaluation-suite": { - title: "Create an evaluation suite", - }, - "run-evaluations": { - title: "Run evaluations", - }, - "capture-mode": { - title: "Capture mode", - }, - "comparative-evaluations": { - title: "Comparative evaluations", - }, -}; - -export default meta; diff --git a/app/en/build/create-tools/evaluate-tools/page.mdx b/app/en/build/create-tools/evaluate-tools/page.mdx deleted file mode 100644 index b8cc52576..000000000 --- a/app/en/build/create-tools/evaluate-tools/page.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: "Evaluate Tools" -description: "Learn how to systematically test and improve your tools with Arcade's evaluation framework" ---- - -import { SubpageList } from '../../../../_components/subpage-list'; -import meta from './_meta'; - -# Evaluate Tools - -Learn how to systematically test and improve your tools to ensure they work reliably in production. Use these techniques after you've built your initial tools to validate their performance and guide iterative improvements. - -<SubpageList basePath="/build/create-tools/evaluate-tools" meta={meta} /> diff --git a/app/en/build/create-tools/secure-your-server/_meta.tsx b/app/en/build/create-tools/secure-your-server/_meta.tsx deleted file mode 100644 index dcf7271d9..000000000 --- a/app/en/build/create-tools/secure-your-server/_meta.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "*": { - theme: { - breadcrumb: true, - toc: true, - copyPage: true, - }, - }, - index: { - title: "Overview", - }, - "secure-your-mcp-server": { - title: "Add Resource Server auth", - }, -}; - -export default meta; diff --git a/app/en/build/create-tools/tool-basics/_meta.tsx b/app/en/build/create-tools/tool-basics/_meta.tsx deleted file mode 100644 index 122df3a14..000000000 --- a/app/en/build/create-tools/tool-basics/_meta.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "*": { - theme: { - breadcrumb: true, - toc: true, - copyPage: true, - }, - }, - index: { - title: "Overview", - }, - "compare-server-types": { - title: "Compare MCP server types", - }, - "build-mcp-server": { - title: "Build an MCP Server to write custom tools", - }, - "create-tool-auth": { - title: "Create a tool with auth", - }, - "create-tool-secrets": { - title: "Create a tool with secrets", - }, - "runtime-data-access": { - title: "Access runtime data", - }, - "call-tools-mcp": { - title: "Call tools from MCP clients", - }, - "organize-mcp-tools": { - title: "Organize your MCP server and tools", - }, - "add-tool-metadata": { - title: "Add metadata to your tools", - }, -}; - -export default meta; diff --git a/app/en/build/tool-calling/_meta.tsx b/app/en/build/tool-calling/_meta.tsx deleted file mode 100644 index 4afb60e1a..000000000 --- a/app/en/build/tool-calling/_meta.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import type { MetaRecord } from "nextra"; - -export const meta: MetaRecord = { - "error-handling": { - title: "Handling errors", - }, - "call-third-party-apis": { - title: "Call third-party APIs", - }, - "custom-apps": { - title: "In custom applications", - }, -}; - -export default meta; diff --git a/app/en/build/tool-calling/custom-apps/_meta.tsx b/app/en/build/tool-calling/custom-apps/_meta.tsx deleted file mode 100644 index 20b94d9f5..000000000 --- a/app/en/build/tool-calling/custom-apps/_meta.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "*": { - theme: { - breadcrumb: true, - toc: true, - copyPage: true, - }, - }, - index: { - title: "Overview", - }, - "auth-tool-calling": { - title: "Authorize tool calling", - }, - "check-auth-status": { - title: "Check authorization status", - }, - "get-tool-definitions": { - title: "Get formatted tool definitions", - }, -}; - -export default meta; diff --git a/app/en/build/user-facing-agents/_meta.tsx b/app/en/build/user-facing-agents/_meta.tsx deleted file mode 100644 index bcb22366f..000000000 --- a/app/en/build/user-facing-agents/_meta.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import type { MetaRecord } from "nextra"; - -export const meta: MetaRecord = { - index: { - title: "Overview", - }, - "secure-auth-production": { - title: "Secure auth in production", - }, -}; - -export default meta; diff --git a/app/en/build/user-facing-agents/page.mdx b/app/en/build/user-facing-agents/page.mdx deleted file mode 100644 index f139190b2..000000000 --- a/app/en/build/user-facing-agents/page.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: "User-facing agents" -description: "Ship agents that act on behalf of your end users, with production-ready auth" ---- - -import { Cards } from "nextra/components"; - -# User-facing agents - -These guides are for **agent developers** shipping agents that act on behalf of end users, rather than only for themselves. Once real users sign in, the authorization flow they see becomes part of your product. - -<Cards num={2}> - <Cards.Card - title="Secure auth in production" - href="/build/user-facing-agents/secure-auth-production" - /> -</Cards> - -## Related - -- [Call tools with user authorization](/build/tool-calling/custom-apps/auth-tool-calling) to trigger and handle auth from your app -- [Auth providers](/references/auth-providers) to use your own OAuth app credentials and brand the consent screen -- [User Sources](/operate/identity/user-sources) if a platform operator connects your organization's identity provider diff --git a/app/en/get-started/_meta.tsx b/app/en/get-started/_meta.tsx deleted file mode 100644 index 589f4674e..000000000 --- a/app/en/get-started/_meta.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import type { MetaRecord } from "nextra"; - -export const meta: MetaRecord = { - "about-arcade": "About Arcade", - "agent-plugin": "Agent Plugin", - setup: "Setup", - quickstarts: "Quickstarts", - "agent-frameworks": "Agent Frameworks", - "mcp-clients": "MCP Clients", -}; - -export default meta; diff --git a/app/en/get-started/agent-frameworks/_meta.tsx b/app/en/get-started/agent-frameworks/_meta.tsx deleted file mode 100644 index d032b8cc7..000000000 --- a/app/en/get-started/agent-frameworks/_meta.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import type { MetaRecord } from "nextra"; - -export const meta: MetaRecord = { - index: { - title: "Overview", - }, - "setup-arcade-with-your-llm-python": { - title: "Setup Arcade with your LLM (Python)", - }, - ag2: { - title: "AG2", - }, - copilotkit: { - title: "CopilotKit", - }, - crewai: { - title: "CrewAI", - }, - "google-adk": { - title: "Google ADK", - }, - langchain: { - title: "LangChain", - }, - mastra: { - title: "Mastra", - }, - "openai-agents": { - title: "OpenAI Agents", - }, - "tanstack-ai": { - title: "TanStack AI", - }, - vercelai: { - title: "Vercel AI SDK", - }, - springai: { - title: "Spring AI SDK", - }, -}; - -export default meta; diff --git a/app/en/get-started/agent-frameworks/ag2/_meta.tsx b/app/en/get-started/agent-frameworks/ag2/_meta.tsx deleted file mode 100644 index e1bebc623..000000000 --- a/app/en/get-started/agent-frameworks/ag2/_meta.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "*": { - theme: { - breadcrumb: true, - toc: true, - copyPage: true, - }, - }, - "use-arcade-tools": { - title: "Setup Arcade tools with AG2", - }, -}; - -export default meta; diff --git a/app/en/get-started/agent-frameworks/crewai/_meta.tsx b/app/en/get-started/agent-frameworks/crewai/_meta.tsx deleted file mode 100644 index 349f4327a..000000000 --- a/app/en/get-started/agent-frameworks/crewai/_meta.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "*": { - theme: { - breadcrumb: true, - toc: true, - copyPage: true, - }, - }, - "use-arcade-tools": { - title: "Setup Arcade tools with CrewAI", - }, -}; - -export default meta; diff --git a/app/en/get-started/agent-frameworks/google-adk/_meta.tsx b/app/en/get-started/agent-frameworks/google-adk/_meta.tsx deleted file mode 100644 index 6b37f150a..000000000 --- a/app/en/get-started/agent-frameworks/google-adk/_meta.tsx +++ /dev/null @@ -1,5 +0,0 @@ -export default { - overview: "Overview", - "setup-python": "Setup (Python)", - "setup-typescript": "Setup (TypeScript)", -}; diff --git a/app/en/get-started/agent-frameworks/langchain/_meta.tsx b/app/en/get-started/agent-frameworks/langchain/_meta.tsx deleted file mode 100644 index cc2bbb33c..000000000 --- a/app/en/get-started/agent-frameworks/langchain/_meta.tsx +++ /dev/null @@ -1,6 +0,0 @@ -export default { - overview: "Overview", - "use-arcade-with-langchain-py": "Setup (Python)", - "use-arcade-with-langchain-ts": "Setup (TypeScript)", - "auth-langchain-tools": "Authorizing Existing Tools", -}; diff --git a/app/en/get-started/agent-frameworks/langchain/overview/page.mdx b/app/en/get-started/agent-frameworks/langchain/overview/page.mdx deleted file mode 100644 index 4b81fe88b..000000000 --- a/app/en/get-started/agent-frameworks/langchain/overview/page.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: "Arcade with LangChain" -description: "Integrate Arcade tools with LangChain agents" ---- - -# Arcade with LangChain - -[LangChain](https://www.langchain.com/) is a popular framework for building AI agents that abstracts much of the complexity of agent development. Arcade integrates with both the Python and JavaScript versions, giving your agents access to Gmail, GitHub, Slack, and 7,500+ other tools. - -## Get started - -Choose your language to set up Arcade with LangChain: - -- **[Python setup](/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py)** - Build an agent with Arcade tools using `langchain-arcade` -- **[TypeScript setup](/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts)** - Build an agent with Arcade tools using `@arcadeai/arcadejs` - -## What you can build - -With Arcade and LangChain, your agents can: - -- Read and send emails via Gmail -- Post messages to Slack channels -- Create GitHub issues and pull requests -- Search the web and extract content -- Access 7,500+ other integrations - -Browse the [full MCP server catalog](/resources/integrations) to see all available tools. - -## Advanced topics - -- **[Authorizing existing tools](/get-started/agent-frameworks/langchain/auth-langchain-tools)** - Add Arcade authorization to your existing LangChain tools diff --git a/app/en/get-started/agent-frameworks/openai-agents/_meta.tsx b/app/en/get-started/agent-frameworks/openai-agents/_meta.tsx deleted file mode 100644 index 6b37f150a..000000000 --- a/app/en/get-started/agent-frameworks/openai-agents/_meta.tsx +++ /dev/null @@ -1,5 +0,0 @@ -export default { - overview: "Overview", - "setup-python": "Setup (Python)", - "setup-typescript": "Setup (TypeScript)", -}; diff --git a/app/en/get-started/agent-plugin/_meta.tsx b/app/en/get-started/agent-plugin/_meta.tsx deleted file mode 100644 index 490a07d94..000000000 --- a/app/en/get-started/agent-plugin/_meta.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import type { MetaRecord } from "nextra"; - -export const meta: MetaRecord = { - index: { - title: "Install", - }, - "whats-included": { - title: "What's included", - }, -}; - -export default meta; diff --git a/app/en/get-started/mcp-clients/page.mdx b/app/en/get-started/mcp-clients/page.mdx deleted file mode 100644 index e0fed70e8..000000000 --- a/app/en/get-started/mcp-clients/page.mdx +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: "Connect to MCP Clients" -description: "Learn how to connect Cursor, Claude Desktop, Claude Code, VS Code, GitHub Copilot, and Copilot Studio to Arcade MCP Gateways over Streamable HTTP." ---- - -import { MCPClientGrid } from "./mcp-client-grid"; - -# Connect to MCP Clients - -You can connect [Arcade MCP servers](/resources/integrations) to MCP-compatible clients and development environments to unlock powerful flows for your agents. - -Every supported MCP client, including Cursor, Claude Desktop, Claude Code, VS Code, GitHub Copilot, and Copilot Studio, connects to an Arcade [MCP Gateway](/operate/governance/mcp-gateways) over the Streamable HTTP transport. The gateway decides which tools the client can call: pick from Arcade-hosted toolkits (GitHub, Linear, Gmail, Slack, Google Calendar, Notion, Jira, Confluence, and dozens more in the [integrations catalog](/resources/integrations)), from [remote MCP servers](/operate/governance/remote-mcp-servers) you registered, and from your own [custom MCP servers](/build/create-tools/tool-basics/build-mcp-server) hosted with [Arcade Deploy](/build/arcade-deploy). - -<MCPClientGrid /> - -## Use the hosted gateway - -To connect your agent to apps through Arcade's hosted gateway, [install the Arcade agent plugin](/get-started/agent-plugin). This guide connects your client to a gateway you configure. diff --git a/app/en/get-started/quickstarts/_meta.tsx b/app/en/get-started/quickstarts/_meta.tsx deleted file mode 100644 index 384c83ddb..000000000 --- a/app/en/get-started/quickstarts/_meta.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { MetaRecord } from "nextra"; - -export const meta: MetaRecord = { - "call-tool-agent": "Call tools in agents", - "call-tool-client": "Call tools in IDE/MCP clients", - "mcp-server-quickstart": "Build an MCP server for custom tools", - "operator-quickstart": { - title: "Set up Arcade for your org", - href: "/operate/quickstart", - }, -}; - -export default meta; diff --git a/app/en/get-started/setup/_meta.tsx b/app/en/get-started/setup/_meta.tsx deleted file mode 100644 index f40713177..000000000 --- a/app/en/get-started/setup/_meta.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { MetaRecord } from "nextra"; - -export const meta: MetaRecord = { - "api-keys": "Get an API key", - "strong-authentication": "Set up strong authentication", - "connect-arcade-docs": "Connect Arcade docs to your IDE", - "windows-environment": "Windows environment setup", -}; - -export default meta; diff --git a/app/en/home/page.mdx b/app/en/home/page.mdx deleted file mode 100644 index dcc287593..000000000 --- a/app/en/home/page.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: "Arcade Docs" -description: "Arcade is the enterprise-ready actions runtime for AI agents. Guides, resources, sample agents, and references for agent authorization, agent-optimized tools (MCP servers), and agent lifecycle governance." ---- - -import { LandingPage } from "./landing-page"; - -<LandingPage /> diff --git a/app/en/layout.tsx b/app/en/layout.tsx new file mode 100644 index 000000000..5328cbf65 --- /dev/null +++ b/app/en/layout.tsx @@ -0,0 +1,21 @@ +import type { ReactNode } from "react"; +import { ChromeLayout } from "@/components/chrome/docs-layout"; +import { buildSidebarModel } from "@/components/chrome/sidebar-model"; +import { SiteFooter } from "@/components/chrome/site-footer"; +import { source } from "@/lib/source"; + +/** + * Docs layout: navbar, sidebar and page area (components/chrome/), followed + * by the site footer band. + */ +export default function Layout({ children }: { children: ReactNode }) { + const tree = source.getPageTree(); + return ( + <> + <ChromeLayout directories={buildSidebarModel(tree, "/en")} tree={tree}> + {children} + </ChromeLayout> + <SiteFooter /> + </> + ); +} diff --git a/app/en/operate/_meta.tsx b/app/en/operate/_meta.tsx deleted file mode 100644 index e9922035b..000000000 --- a/app/en/operate/_meta.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import type { MetaRecord } from "nextra"; - -export const meta: MetaRecord = { - index: { - title: "Overview", - }, - quickstart: { - title: "Quickstart", - }, - deploy: { - title: "Deploy", - }, - identity: { - title: "Identity", - }, - governance: { - title: "Governance", - }, -}; - -export default meta; diff --git a/app/en/operate/deploy/_meta.tsx b/app/en/operate/deploy/_meta.tsx deleted file mode 100644 index 5a96c101e..000000000 --- a/app/en/operate/deploy/_meta.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - index: { - title: "Overview", - }, - architecture: { - title: "Platform architecture", - }, - "arcade-cloud": { - title: "Arcade Cloud", - }, - azure: { - title: "Azure Marketplace", - }, - aws: { - title: "AWS Marketplace", - }, - gcp: { - title: "GCP (coming soon)", - }, - helm: { - title: "Self-host with Helm", - }, - "helm-arcade-deploy": { - title: "Arcade Deploy on your own cluster (Helm)", - }, - worker: { - title: "Arcade Worker", - }, - "on-prem": { - title: "Hybrid MCP servers", - }, -}; - -export default meta; diff --git a/app/en/operate/deploy/helm/_meta.tsx b/app/en/operate/deploy/helm/_meta.tsx deleted file mode 100644 index 6d4387ad7..000000000 --- a/app/en/operate/deploy/helm/_meta.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import type { MetaRecord } from "nextra"; - -export const meta: MetaRecord = { - "internal-networking": { - title: "Internal networking", - }, - "ipv6-dual-stack": { - title: "IPv6 and dual-stack", - }, - "container-registry": { - title: "Use your own container registry", - }, - "values-reference": { - title: "Values reference", - }, -}; - -export default meta; diff --git a/app/en/operate/governance/_meta.tsx b/app/en/operate/governance/_meta.tsx deleted file mode 100644 index 96ec04268..000000000 --- a/app/en/operate/governance/_meta.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import type { MetaRecord } from "nextra"; - -export const meta: MetaRecord = { - index: { - title: "Overview", - }, - "mcp-gateways": { - title: "MCP Gateways", - }, - "remote-mcp-servers": { - title: "Remote MCP servers", - }, - "private-registry": { - title: "Private Registry", - }, - "contextual-access": { - title: "Contextual Access", - }, - "audit-logs": { - title: "Audit Logs", - }, - "tool-executions": { - title: "Tool Executions", - }, -}; - -export default meta; diff --git a/app/en/operate/governance/contextual-access/_meta.tsx b/app/en/operate/governance/contextual-access/_meta.tsx deleted file mode 100644 index 9c26acccb..000000000 --- a/app/en/operate/governance/contextual-access/_meta.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import type { MetaRecord } from "nextra"; - -export const meta: MetaRecord = { - "how-hooks-work": { - title: "How Hooks Work", - }, - "rate-limiting": { - title: "Rate Limiting", - }, - "group-access": { - title: "Group-Based Tool Access", - }, - examples: { - title: "Running an Extension", - }, - "build-your-own": { - title: "Build Your Own", - }, -}; - -export default meta; diff --git a/app/en/operate/governance/mcp-gateways/_meta.tsx b/app/en/operate/governance/mcp-gateways/_meta.tsx deleted file mode 100644 index 95edb35e2..000000000 --- a/app/en/operate/governance/mcp-gateways/_meta.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import type { MetaRecord } from "nextra"; - -export const meta: MetaRecord = { - "create-via-dashboard": { - title: "Create via Dashboard", - }, - "create-via-ai": { - title: "Create via AI Assistant", - }, - "tool-recommendation": { - title: "Tool Recommendation", - }, -}; - -export default meta; diff --git a/app/en/operate/governance/mcp-gateways/tool-recommendation/_meta.tsx b/app/en/operate/governance/mcp-gateways/tool-recommendation/_meta.tsx deleted file mode 100644 index aca92b6e6..000000000 --- a/app/en/operate/governance/mcp-gateways/tool-recommendation/_meta.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import type { MetaRecord } from "nextra"; - -export const meta: MetaRecord = { - index: { - title: "Overview", - }, - configure: { - title: "Configure", - }, - limitations: { - title: "Limitations", - }, -}; - -export default meta; diff --git a/app/en/operate/governance/page.mdx b/app/en/operate/governance/page.mdx deleted file mode 100644 index 020c4b127..000000000 --- a/app/en/operate/governance/page.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: "Governance" -description: "Federate tools, enforce access policies, and review administrative actions and tool runs" ---- - -import { Cards } from "nextra/components"; - -# Governance - -Control which tools agents can use, enforce policies at runtime, and review administrative actions and the tool runs they produced. - -<Cards num={2}> - <Cards.Card title="MCP Gateways" href="/operate/governance/mcp-gateways" /> - <Cards.Card - title="Remote MCP servers" - href="/operate/governance/remote-mcp-servers" - /> - <Cards.Card - title="Private Registry" - href="/operate/governance/private-registry" - /> - <Cards.Card - title="Contextual Access" - href="/operate/governance/contextual-access" - /> - <Cards.Card title="Audit Logs" href="/operate/governance/audit-logs" /> - <Cards.Card - title="Tool Executions" - href="/operate/governance/tool-executions" - /> -</Cards> diff --git a/app/en/operate/governance/remote-mcp-servers/_meta.tsx b/app/en/operate/governance/remote-mcp-servers/_meta.tsx deleted file mode 100644 index 34ac5af52..000000000 --- a/app/en/operate/governance/remote-mcp-servers/_meta.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - index: { - title: "Overview", - }, - salesforce: { - title: "Salesforce", - }, - servicenow: { - title: "ServiceNow", - }, - "dynamics-365-customer-service": { - title: "Dynamics 365 Customer Service", - }, - hubspot: { - title: "HubSpot", - }, - snowflake: { - title: "Snowflake", - }, - atlassian: { - title: "Atlassian", - }, - github: { - title: "GitHub", - }, - splunk: { - title: "Splunk", - }, - aws: { - title: "AWS", - }, -}; - -export default meta; diff --git a/app/en/operate/identity/_meta.tsx b/app/en/operate/identity/_meta.tsx deleted file mode 100644 index d4d39d93d..000000000 --- a/app/en/operate/identity/_meta.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import type { MetaRecord } from "nextra"; - -export const meta: MetaRecord = { - index: { - title: "Overview", - }, - "user-sources": { - title: "User Sources", - }, -}; - -export default meta; diff --git a/app/en/operate/identity/page.mdx b/app/en/operate/identity/page.mdx deleted file mode 100644 index e1080cd65..000000000 --- a/app/en/operate/identity/page.mdx +++ /dev/null @@ -1,19 +0,0 @@ ---- -title: "Identity" -description: "Connect end-user identity providers and configure how gateways authenticate users" ---- - -import { Cards } from "nextra/components"; - -# Identity - -Configure how end users authenticate to Arcade gateways and how your organization manages auth providers. - -<Cards num={2}> - <Cards.Card title="User Sources" href="/operate/identity/user-sources" /> - <Cards.Card title="Auth providers" href="/references/auth-providers" /> - <Cards.Card - title="Secure auth in production" - href="/build/user-facing-agents/secure-auth-production" - /> -</Cards> diff --git a/app/en/operate/identity/user-sources/_meta.tsx b/app/en/operate/identity/user-sources/_meta.tsx deleted file mode 100644 index 66f7281e9..000000000 --- a/app/en/operate/identity/user-sources/_meta.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import type { MetaRecord } from "nextra"; - -export const meta: MetaRecord = { - index: { - title: "Overview", - }, - auth0: { - title: "Auth0", - }, - clerk: { - title: "Clerk", - }, - "microsoft-entra-id": { - title: "Microsoft Entra ID", - }, - okta: { - title: "Okta", - }, - stytch: { - title: "Stytch", - }, -}; - -export default meta; diff --git a/app/en/operate/page.mdx b/app/en/operate/page.mdx deleted file mode 100644 index 969ee503c..000000000 --- a/app/en/operate/page.mdx +++ /dev/null @@ -1,72 +0,0 @@ ---- -title: "Operate Arcade" -description: "Deploy, secure, and govern Arcade for your organization. Written for platform operators." ---- - -import { Cards } from "nextra/components"; - -# Operate Arcade - -These docs are for **platform operators**: the people who deploy Arcade, connect identity providers, and govern how agents use tools across a company. - -If you build agents or custom MCP servers, start with [Get Started](/get-started/about-arcade) and [Build](/build). - -## Choose your path - -<Cards num={2}> - <Cards.Card - title="Quickstart: stand up Arcade for your org" - href="/operate/quickstart" - /> - <Cards.Card - title="Compare hosting options" - href="/operate/deploy" - /> -</Cards> - -## What operators configure - -### Deploy - -Run Arcade Cloud, deploy through a cloud marketplace, or self-host with Helm. - -<Cards num={2}> - <Cards.Card title="Deployment & hosting" href="/operate/deploy" /> - <Cards.Card title="Arcade Cloud" href="/operate/deploy/arcade-cloud" /> - <Cards.Card title="Self-host with Helm" href="/operate/deploy/helm" /> - <Cards.Card title="Hybrid MCP servers" href="/operate/deploy/on-prem" /> -</Cards> - -### Identity - -Connect your end-user identity provider and configure how gateways authenticate users. - -<Cards num={2}> - <Cards.Card title="User Sources" href="/operate/identity/user-sources" /> - <Cards.Card title="Auth providers" href="/references/auth-providers" /> - <Cards.Card - title="Secure auth in production" - href="/build/user-facing-agents/secure-auth-production" - /> -</Cards> - -### Governance - -Federate tools, enforce access policies, and review administrative actions and the tool runs they produced. - -<Cards num={2}> - <Cards.Card title="MCP Gateways" href="/operate/governance/mcp-gateways" /> - <Cards.Card - title="Remote MCP servers" - href="/operate/governance/remote-mcp-servers" - /> - <Cards.Card title="Contextual Access" href="/operate/governance/contextual-access" /> - <Cards.Card title="Audit Logs" href="/operate/governance/audit-logs" /> - <Cards.Card title="Tool Executions" href="/operate/governance/tool-executions" /> -</Cards> - -## Related references - -- [Glossary](/resources/glossary): Account, Tenant, Project, User, and User Source -- [API reference](/references/api): Administrator and runtime APIs -- [FAQ](/resources/faq): common operator and developer questions diff --git a/app/en/references/_meta.tsx b/app/en/references/_meta.tsx deleted file mode 100644 index e2c1267e8..000000000 --- a/app/en/references/_meta.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import type { MetaRecord } from "nextra"; - -export const meta: MetaRecord = { - index: { - title: "Overview", - }, - changelog: { - title: "Changelog", - href: "https://releases.arcade.dev/preview/", - }, - api: { - title: "API", - }, - mcp: { - title: "Arcade MCP", - }, - "arcade-cli": { - title: "Arcade CLI", - }, - "cli-cheat-sheet": { - title: "CLI Cheat Sheet", - }, - "contextual-access-webhook-api": { - title: "Contextual Access Webhook API", - }, - "auth-providers": { - title: "Auth Providers", - }, -}; - -export default meta; diff --git a/app/en/references/auth-providers/_meta.tsx b/app/en/references/auth-providers/_meta.tsx deleted file mode 100644 index 89d0266eb..000000000 --- a/app/en/references/auth-providers/_meta.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - index: { - title: "Overview", - }, - oauth2: { - title: "OAuth 2.0", - }, -}; - -export default meta; diff --git a/app/en/references/mcp/_meta.tsx b/app/en/references/mcp/_meta.tsx deleted file mode 100644 index 5832a5e47..000000000 --- a/app/en/references/mcp/_meta.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "*": { - theme: { - breadcrumb: true, - toc: true, - copyPage: true, - }, - }, - python: { - title: "Python", - }, - telemetry: { - title: "Telemetry", - }, -}; - -export default meta; diff --git a/app/en/references/mcp/python/_meta.tsx b/app/en/references/mcp/python/_meta.tsx deleted file mode 100644 index 1abfb3518..000000000 --- a/app/en/references/mcp/python/_meta.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "*": { - theme: { - breadcrumb: true, - layout: "full", - toc: true, - copyPage: true, - }, - }, - index: { - title: "Overview", - }, - context: { - title: "Context", - }, - resources: { - title: "Resources", - }, - server: { - title: "Server", - }, - settings: { - title: "Settings", - }, - middleware: { - title: "Middleware", - }, - errors: { - title: "Errors", - }, -}; - -export default meta; diff --git a/app/en/resources/_meta.tsx b/app/en/resources/_meta.tsx deleted file mode 100644 index 0708cde75..000000000 --- a/app/en/resources/_meta.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import type { MetaRecord } from "nextra"; - -export const meta: MetaRecord = { - integrations: { - title: "Integrations", - }, - examples: { - title: "Examples", - }, - tools: { - title: "Tools", - }, - glossary: { - title: "Glossary", - }, - faq: { - title: "FAQ", - }, - "security-research-program": { - title: "Security research program", - }, - "contact-us": { - title: "Contact", - }, - "registry-early-access": { - display: "hidden", - }, - "early-access": { - title: "Early Access", - }, -}; - -export default meta; diff --git a/app/en/resources/contact-us/page.mdx b/app/en/resources/contact-us/page.mdx deleted file mode 100644 index 497940320..000000000 --- a/app/en/resources/contact-us/page.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: "Contact Us" -description: "Get support and connect with the Arcade team through various channels" ---- - -import { ContactCards } from "./contact-cards"; - -# Contact Us - -**We're here to help you (and your agents) succeed!** -<ContactCards /> diff --git a/app/en/resources/early-access/_meta.tsx b/app/en/resources/early-access/_meta.tsx deleted file mode 100644 index 706bcf131..000000000 --- a/app/en/resources/early-access/_meta.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - registry: { - title: "Arcade Registry", - href: "/en/resources/registry-early-access", - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/_lib/toolkit-docs-page.tsx b/app/en/resources/integrations/_lib/toolkit-docs-page.tsx deleted file mode 100644 index ec8227ce0..000000000 --- a/app/en/resources/integrations/_lib/toolkit-docs-page.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import type { Metadata } from "next"; -import { notFound } from "next/navigation"; -import { ToolkitPage } from "@/app/_components/toolkit-docs"; -import { readToolkitData, toToolkitSummary } from "@/app/_lib/toolkit-data"; -import { - getToolkitCanonicalPath, - getToolkitStaticParamsForCategory, -} from "@/app/_lib/toolkit-static-params"; -import type { IntegrationCategory } from "@/toolkit-docs-generator/src/shared/toolkit-primitives"; - -type ToolkitDocsParams = { - toolkitId: string; -}; - -export function createToolkitDocsPage(category: IntegrationCategory) { - const getToolkitData = (toolkitId: string) => readToolkitData(toolkitId); - - const generateStaticParams = async () => - await getToolkitStaticParamsForCategory(category); - - const generateMetadata = async ({ - params, - }: { - params: Promise<ToolkitDocsParams>; - }): Promise<Metadata> => { - const { toolkitId } = await params; - const data = await getToolkitData(toolkitId); - if (!data) { - return {}; - } - - // Canonicalize to the toolkit's own category + slug, not the URL it was - // reached through. The dynamic [toolkitId] route accepts any category, so a - // wrong-category alias (e.g. development/pagerduty-api for a customer-support - // toolkit) must point at the one generated, index-linked page. - const canonical = getToolkitCanonicalPath({ - id: data.id, - category: data.metadata?.category, - docsLink: data.metadata?.docsLink, - }); - - const metadata: Metadata = { - title: data.label || data.id, - description: data.description || "Generated MCP server documentation.", - alternates: { canonical }, - }; - - // Hidden toolkits stay reachable via the dynamic route (and render as - // non-clickable cards in the index), but must not be indexed — otherwise - // their self-canonical is flagged as an orphan with no incoming links. - if (data.metadata?.isHidden) { - metadata.robots = { index: false }; - } - - return metadata; - }; - - const Page = async ({ params }: { params: Promise<ToolkitDocsParams> }) => { - const { toolkitId } = await params; - const data = await getToolkitData(toolkitId); - - if (!data) { - notFound(); - } - - // Pass a summary (per-tool detail stripped) so the heavy fields never enter - // the initial Flight payload — detail is fetched on expand. See MARTECH-17. - return <ToolkitPage data={toToolkitSummary(data)} />; - }; - - return { generateMetadata, generateStaticParams, Page }; -} diff --git a/app/en/resources/integrations/_meta.tsx b/app/en/resources/integrations/_meta.tsx deleted file mode 100644 index 3b6e8095d..000000000 --- a/app/en/resources/integrations/_meta.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "*": { - theme: { - breadcrumb: true, - toc: true, - copyPage: true, - }, - }, - index: { - title: "Overview", - theme: { - toc: false, - copyPage: false, - }, - }, - productivity: { - title: "Productivity & Docs", - }, - social: { - title: "Social & Communication", - }, - entertainment: { - title: "Entertainment", - }, - development: { - title: "Developer Tools", - }, - payments: { - title: "Payments & Finance", - }, - search: { - title: "Search Tools", - }, - sales: { - title: "Sales", - }, - databases: { - title: "Databases", - }, - "customer-support": { - title: "Customer Support", - }, - "-- Feedback": { - type: "separator", - title: "Feedback", - }, - "tool-feedback": { - title: "Tool Feedback", - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/create-category-meta.ts b/app/en/resources/integrations/create-category-meta.ts deleted file mode 100644 index 0ee8d4720..000000000 --- a/app/en/resources/integrations/create-category-meta.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { MetaRecord } from "nextra"; - -type ToolkitType = - | "arcade" - | "arcade_starter" - | "community" - | "verified" - | "auth"; - -export type CategoryEntry = { - slug: string; - title: string; - href: string; - type: ToolkitType; -}; - -/** - * Builds a Nextra MetaRecord for an integration category, automatically - * inserting "Optimized" and "Starter" separator headings based on toolkit type. - * - * - "Optimized" group: any entry whose type is not "arcade_starter" - * - "Starter" group: entries with type "arcade_starter" - * - * A separator is only emitted when its group is non-empty, so categories - * with a single type do not show an unnecessary heading. - */ -export function createCategoryMeta(entries: CategoryEntry[]): MetaRecord { - const optimized = entries.filter((e) => e.type !== "arcade_starter"); - const starter = entries.filter((e) => e.type === "arcade_starter"); - - const result: MetaRecord = {}; - - if (optimized.length > 0) { - result["-- Optimized"] = { type: "separator", title: "Optimized" }; - for (const entry of optimized) { - result[entry.slug] = { title: entry.title, href: entry.href }; - } - } - - if (starter.length > 0) { - result["-- Starter"] = { type: "separator", title: "Starter" }; - for (const entry of starter) { - result[entry.slug] = { title: entry.title, href: entry.href }; - } - } - - return result; -} diff --git a/app/en/resources/integrations/customer-support/[toolkitId]/_meta.tsx b/app/en/resources/integrations/customer-support/[toolkitId]/_meta.tsx deleted file mode 100644 index 5e2130851..000000000 --- a/app/en/resources/integrations/customer-support/[toolkitId]/_meta.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { MetaRecord } from "nextra"; -import { toolkitPageTheme } from "../../toolkit-page-theme"; - -const meta: MetaRecord = { - "*": { - theme: toolkitPageTheme, - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/customer-support/[toolkitId]/page-impl.tsx b/app/en/resources/integrations/customer-support/[toolkitId]/page-impl.tsx deleted file mode 100644 index 209ea7b4a..000000000 --- a/app/en/resources/integrations/customer-support/[toolkitId]/page-impl.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { createToolkitDocsPage } from "@/app/en/resources/integrations/_lib/toolkit-docs-page"; - -export const dynamicParams = false; - -const { Page, generateMetadata, generateStaticParams } = - createToolkitDocsPage("customer-support"); - -export { generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/customer-support/[toolkitId]/page.mdx b/app/en/resources/integrations/customer-support/[toolkitId]/page.mdx deleted file mode 100644 index ca97c319f..000000000 --- a/app/en/resources/integrations/customer-support/[toolkitId]/page.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "MCP server" -description: "Generated MCP server documentation." ---- - -import Page, { - dynamicParams, - generateMetadata, - generateStaticParams, -} from "./page-impl"; - -export { dynamicParams, generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/customer-support/_meta.tsx b/app/en/resources/integrations/customer-support/_meta.tsx deleted file mode 100644 index 79795beb0..000000000 --- a/app/en/resources/integrations/customer-support/_meta.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "-- Optimized": { - type: "separator", - title: "Optimized", - }, - customerio: { - title: "Customer.io", - href: "/en/resources/integrations/customer-support/customerio", - }, - freshdesk: { - title: "Freshdesk", - href: "/en/resources/integrations/customer-support/freshdesk", - }, - freshservice: { - title: "Freshservice", - href: "/en/resources/integrations/customer-support/freshservice", - }, - pylon: { - title: "Pylon", - href: "/en/resources/integrations/customer-support/pylon", - }, - servicenow: { - title: "ServiceNow", - href: "/en/resources/integrations/customer-support/servicenow", - }, - zendesk: { - title: "Zendesk", - href: "/en/resources/integrations/customer-support/zendesk", - }, - "-- Starter": { - type: "separator", - title: "Starter", - }, - "customerio-api": { - title: "Customer.io API", - href: "/en/resources/integrations/customer-support/customerio-api", - }, - "customerio-pipelines-api": { - title: "Customer.io Pipelines API", - href: "/en/resources/integrations/customer-support/customerio-pipelines-api", - }, - "customerio-track-api": { - title: "Customer.io Track API", - href: "/en/resources/integrations/customer-support/customerio-track-api", - }, - "freshservice-api": { - title: "Freshservice API", - href: "/en/resources/integrations/customer-support/freshservice-api", - }, - "intercom-api": { - title: "Intercom API", - href: "/en/resources/integrations/customer-support/intercom-api", - }, - "pagerduty-api": { - title: "PagerDuty API", - href: "/en/resources/integrations/customer-support/pagerduty-api", - }, - "pylon-api": { - title: "Pylon API", - href: "/en/resources/integrations/customer-support/pylon-api", - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/databases/[toolkitId]/_meta.tsx b/app/en/resources/integrations/databases/[toolkitId]/_meta.tsx deleted file mode 100644 index 5e2130851..000000000 --- a/app/en/resources/integrations/databases/[toolkitId]/_meta.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { MetaRecord } from "nextra"; -import { toolkitPageTheme } from "../../toolkit-page-theme"; - -const meta: MetaRecord = { - "*": { - theme: toolkitPageTheme, - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/databases/[toolkitId]/page-impl.tsx b/app/en/resources/integrations/databases/[toolkitId]/page-impl.tsx deleted file mode 100644 index 9af8e0250..000000000 --- a/app/en/resources/integrations/databases/[toolkitId]/page-impl.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { createToolkitDocsPage } from "@/app/en/resources/integrations/_lib/toolkit-docs-page"; - -export const dynamicParams = false; - -const { Page, generateMetadata, generateStaticParams } = - createToolkitDocsPage("databases"); - -export { generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/databases/[toolkitId]/page.mdx b/app/en/resources/integrations/databases/[toolkitId]/page.mdx deleted file mode 100644 index ca97c319f..000000000 --- a/app/en/resources/integrations/databases/[toolkitId]/page.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "MCP server" -description: "Generated MCP server documentation." ---- - -import Page, { - dynamicParams, - generateMetadata, - generateStaticParams, -} from "./page-impl"; - -export { dynamicParams, generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/databases/_meta.tsx b/app/en/resources/integrations/databases/_meta.tsx deleted file mode 100644 index 2ac324f32..000000000 --- a/app/en/resources/integrations/databases/_meta.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "-- Optimized": { - type: "separator", - title: "Optimized", - }, - clickhouse: { - title: "Clickhouse", - href: "/en/resources/integrations/databases/clickhouse", - }, - mongodb: { - title: "MongoDB", - href: "/en/resources/integrations/databases/mongodb", - }, - postgres: { - title: "Postgres", - href: "/en/resources/integrations/databases/postgres", - }, - snowflake: { - title: "Snowflake", - href: "/en/resources/integrations/databases/snowflake", - }, - yugabytedb: { - title: "YugabyteDB", - href: "/en/resources/integrations/databases/yugabytedb", - }, - "-- Starter": { - type: "separator", - title: "Starter", - }, - "weaviate-api": { - title: "Weaviate API", - href: "/en/resources/integrations/databases/weaviate-api", - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/development/[toolkitId]/_meta.tsx b/app/en/resources/integrations/development/[toolkitId]/_meta.tsx deleted file mode 100644 index 5e2130851..000000000 --- a/app/en/resources/integrations/development/[toolkitId]/_meta.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { MetaRecord } from "nextra"; -import { toolkitPageTheme } from "../../toolkit-page-theme"; - -const meta: MetaRecord = { - "*": { - theme: toolkitPageTheme, - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/development/[toolkitId]/page-impl.tsx b/app/en/resources/integrations/development/[toolkitId]/page-impl.tsx deleted file mode 100644 index 993120891..000000000 --- a/app/en/resources/integrations/development/[toolkitId]/page-impl.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { createToolkitDocsPage } from "@/app/en/resources/integrations/_lib/toolkit-docs-page"; - -export const dynamicParams = false; - -const { Page, generateMetadata, generateStaticParams } = - createToolkitDocsPage("development"); - -export { generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/development/[toolkitId]/page.mdx b/app/en/resources/integrations/development/[toolkitId]/page.mdx deleted file mode 100644 index ca97c319f..000000000 --- a/app/en/resources/integrations/development/[toolkitId]/page.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "MCP server" -description: "Generated MCP server documentation." ---- - -import Page, { - dynamicParams, - generateMetadata, - generateStaticParams, -} from "./page-impl"; - -export { dynamicParams, generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/development/_meta.tsx b/app/en/resources/integrations/development/_meta.tsx deleted file mode 100644 index ba00457dd..000000000 --- a/app/en/resources/integrations/development/_meta.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "-- Optimized": { - type: "separator", - title: "Optimized", - }, - brightdata: { - title: "Bright Data", - href: "/en/resources/integrations/development/brightdata", - }, - "cursor-agents": { - title: "Cursor Agents", - href: "/en/resources/integrations/development/cursor-agents", - }, - datadog: { - title: "Datadog", - href: "/en/resources/integrations/development/datadog", - }, - daytona: { - title: "Daytona", - href: "/en/resources/integrations/development/daytona", - }, - e2b: { - title: "E2B", - href: "/en/resources/integrations/development/e2b", - }, - firecrawl: { - title: "Firecrawl", - href: "/en/resources/integrations/development/firecrawl", - }, - "fly-io": { - title: "Fly.io", - href: "/en/resources/integrations/development/fly-io", - }, - github: { - title: "GitHub", - href: "/en/resources/integrations/development/github", - }, - math: { - title: "Math", - href: "/en/resources/integrations/development/math", - }, - mixpanel: { - title: "Mixpanel", - href: "/en/resources/integrations/development/mixpanel", - }, - pagerduty: { - title: "PagerDuty", - href: "/en/resources/integrations/development/pagerduty", - }, - posthog: { - title: "PostHog", - href: "/en/resources/integrations/development/posthog", - }, - postman: { - title: "Postman", - href: "/en/resources/integrations/development/postman", - }, - vercel: { - title: "Vercel", - href: "/en/resources/integrations/development/vercel", - }, - "-- Starter": { - type: "separator", - title: "Starter", - }, - "cursor-agents-api": { - title: "Cursor Agents API", - href: "/en/resources/integrations/development/cursor-agents-api", - }, - "datadog-api": { - title: "Datadog API", - href: "/en/resources/integrations/development/datadog-api", - }, - "github-api": { - title: "GitHub API", - href: "/en/resources/integrations/development/github-api", - }, - "posthog-api": { - title: "PostHog API", - href: "/en/resources/integrations/development/posthog-api", - }, - "vercel-api": { - title: "Vercel API", - href: "/en/resources/integrations/development/vercel-api", - }, - "zoho-creator-api": { - title: "Zoho Creator API", - href: "/en/resources/integrations/development/zoho-creator-api", - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/entertainment/[toolkitId]/_meta.tsx b/app/en/resources/integrations/entertainment/[toolkitId]/_meta.tsx deleted file mode 100644 index 5e2130851..000000000 --- a/app/en/resources/integrations/entertainment/[toolkitId]/_meta.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { MetaRecord } from "nextra"; -import { toolkitPageTheme } from "../../toolkit-page-theme"; - -const meta: MetaRecord = { - "*": { - theme: toolkitPageTheme, - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/entertainment/[toolkitId]/page-impl.tsx b/app/en/resources/integrations/entertainment/[toolkitId]/page-impl.tsx deleted file mode 100644 index b3fc5986b..000000000 --- a/app/en/resources/integrations/entertainment/[toolkitId]/page-impl.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { createToolkitDocsPage } from "@/app/en/resources/integrations/_lib/toolkit-docs-page"; - -export const dynamicParams = false; - -const { Page, generateMetadata, generateStaticParams } = - createToolkitDocsPage("entertainment"); - -export { generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/entertainment/[toolkitId]/page.mdx b/app/en/resources/integrations/entertainment/[toolkitId]/page.mdx deleted file mode 100644 index ca97c319f..000000000 --- a/app/en/resources/integrations/entertainment/[toolkitId]/page.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "MCP server" -description: "Generated MCP server documentation." ---- - -import Page, { - dynamicParams, - generateMetadata, - generateStaticParams, -} from "./page-impl"; - -export { dynamicParams, generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/entertainment/_meta.tsx b/app/en/resources/integrations/entertainment/_meta.tsx deleted file mode 100644 index 0cc59f8ae..000000000 --- a/app/en/resources/integrations/entertainment/_meta.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "-- Optimized": { - type: "separator", - title: "Optimized", - }, - imgflip: { - title: "Imgflip", - href: "/en/resources/integrations/entertainment/imgflip", - }, - spotify: { - title: "Spotify", - href: "/en/resources/integrations/entertainment/spotify", - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/payments/[toolkitId]/_meta.tsx b/app/en/resources/integrations/payments/[toolkitId]/_meta.tsx deleted file mode 100644 index 5e2130851..000000000 --- a/app/en/resources/integrations/payments/[toolkitId]/_meta.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { MetaRecord } from "nextra"; -import { toolkitPageTheme } from "../../toolkit-page-theme"; - -const meta: MetaRecord = { - "*": { - theme: toolkitPageTheme, - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/payments/[toolkitId]/page-impl.tsx b/app/en/resources/integrations/payments/[toolkitId]/page-impl.tsx deleted file mode 100644 index de3a2ba00..000000000 --- a/app/en/resources/integrations/payments/[toolkitId]/page-impl.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { createToolkitDocsPage } from "@/app/en/resources/integrations/_lib/toolkit-docs-page"; - -export const dynamicParams = false; - -const { Page, generateMetadata, generateStaticParams } = - createToolkitDocsPage("payments"); - -export { generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/payments/[toolkitId]/page.mdx b/app/en/resources/integrations/payments/[toolkitId]/page.mdx deleted file mode 100644 index ca97c319f..000000000 --- a/app/en/resources/integrations/payments/[toolkitId]/page.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "MCP server" -description: "Generated MCP server documentation." ---- - -import Page, { - dynamicParams, - generateMetadata, - generateStaticParams, -} from "./page-impl"; - -export { dynamicParams, generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/payments/_meta.tsx b/app/en/resources/integrations/payments/_meta.tsx deleted file mode 100644 index 64de9299e..000000000 --- a/app/en/resources/integrations/payments/_meta.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "-- Optimized": { - type: "separator", - title: "Optimized", - }, - stripe: { - title: "Stripe", - href: "/en/resources/integrations/payments/stripe", - }, - "-- Starter": { - type: "separator", - title: "Starter", - }, - stripe_api: { - title: "Stripe API", - href: "/en/resources/integrations/payments/stripe_api", - }, - "zoho-books-api": { - title: "Zoho Books API", - href: "/en/resources/integrations/payments/zoho-books-api", - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/productivity/[toolkitId]/_meta.tsx b/app/en/resources/integrations/productivity/[toolkitId]/_meta.tsx deleted file mode 100644 index 5e2130851..000000000 --- a/app/en/resources/integrations/productivity/[toolkitId]/_meta.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { MetaRecord } from "nextra"; -import { toolkitPageTheme } from "../../toolkit-page-theme"; - -const meta: MetaRecord = { - "*": { - theme: toolkitPageTheme, - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/productivity/[toolkitId]/page-impl.tsx b/app/en/resources/integrations/productivity/[toolkitId]/page-impl.tsx deleted file mode 100644 index f111497cc..000000000 --- a/app/en/resources/integrations/productivity/[toolkitId]/page-impl.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { createToolkitDocsPage } from "@/app/en/resources/integrations/_lib/toolkit-docs-page"; - -export const dynamicParams = false; - -const { Page, generateMetadata, generateStaticParams } = - createToolkitDocsPage("productivity"); - -export { generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/productivity/[toolkitId]/page.mdx b/app/en/resources/integrations/productivity/[toolkitId]/page.mdx deleted file mode 100644 index ca97c319f..000000000 --- a/app/en/resources/integrations/productivity/[toolkitId]/page.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "MCP server" -description: "Generated MCP server documentation." ---- - -import Page, { - dynamicParams, - generateMetadata, - generateStaticParams, -} from "./page-impl"; - -export { dynamicParams, generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/productivity/_meta.tsx b/app/en/resources/integrations/productivity/_meta.tsx deleted file mode 100644 index ffd685879..000000000 --- a/app/en/resources/integrations/productivity/_meta.tsx +++ /dev/null @@ -1,198 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "-- Optimized": { - type: "separator", - title: "Optimized", - }, - airtable: { - title: "Airtable", - href: "/en/resources/integrations/productivity/airtable", - }, - asana: { - title: "Asana", - href: "/en/resources/integrations/productivity/asana", - }, - ashby: { - title: "Ashby", - href: "/en/resources/integrations/productivity/ashby", - }, - calendly: { - title: "Calendly", - href: "/en/resources/integrations/productivity/calendly", - }, - clickup: { - title: "ClickUp", - href: "/en/resources/integrations/productivity/clickup", - }, - confluence: { - title: "Confluence", - href: "/en/resources/integrations/productivity/confluence", - }, - dropbox: { - title: "Dropbox", - href: "/en/resources/integrations/productivity/dropbox", - }, - figma: { - title: "Figma", - href: "/en/resources/integrations/productivity/figma", - }, - fireflies: { - title: "Fireflies", - href: "/en/resources/integrations/productivity/fireflies", - }, - forkable: { - title: "Forkable", - href: "/en/resources/integrations/productivity/forkable", - }, - gmail: { - title: "Gmail", - href: "/en/resources/integrations/productivity/gmail", - }, - "google-calendar": { - title: "Google Calendar", - href: "/en/resources/integrations/productivity/google-calendar", - }, - "google-contacts": { - title: "Google Contacts", - href: "/en/resources/integrations/productivity/google-contacts", - }, - "google-docs": { - title: "Google Docs", - href: "/en/resources/integrations/productivity/google-docs", - }, - "google-drive": { - title: "Google Drive", - href: "/en/resources/integrations/productivity/google-drive", - }, - "google-sheets": { - title: "Google Sheets", - href: "/en/resources/integrations/productivity/google-sheets", - }, - "google-slides": { - title: "Google Slides", - href: "/en/resources/integrations/productivity/google-slides", - }, - granola: { - title: "Granola", - href: "/en/resources/integrations/productivity/granola", - }, - jira: { - title: "Jira", - href: "/en/resources/integrations/productivity/jira", - }, - linear: { - title: "Linear", - href: "/en/resources/integrations/productivity/linear", - }, - "microsoft-excel": { - title: "Microsoft Excel", - href: "/en/resources/integrations/productivity/microsoft-excel", - }, - "microsoft-onedrive": { - title: "Microsoft OneDrive", - href: "/en/resources/integrations/productivity/microsoft-onedrive", - }, - "microsoft-outlook-calendar": { - title: "Microsoft Outlook Calendar", - href: "/en/resources/integrations/productivity/microsoft-outlook-calendar", - }, - "microsoft-outlook-mail": { - title: "Microsoft Outlook Mail", - href: "/en/resources/integrations/productivity/microsoft-outlook-mail", - }, - "microsoft-power-bi": { - title: "Microsoft Power BI", - href: "/en/resources/integrations/productivity/microsoft-power-bi", - }, - "microsoft-powerpoint": { - title: "Microsoft PowerPoint", - href: "/en/resources/integrations/productivity/microsoft-powerpoint", - }, - "microsoft-sharepoint": { - title: "Microsoft SharePoint", - href: "/en/resources/integrations/productivity/microsoft-sharepoint", - }, - "microsoft-users": { - title: "Microsoft Users", - href: "/en/resources/integrations/productivity/microsoft-users", - }, - "microsoft-word": { - title: "Microsoft Word", - href: "/en/resources/integrations/productivity/microsoft-word", - }, - notion: { - title: "Notion", - href: "/en/resources/integrations/productivity/notion", - }, - resend: { - title: "Resend", - href: "/en/resources/integrations/productivity/resend", - }, - workday: { - title: "Workday", - href: "/en/resources/integrations/productivity/workday", - }, - "-- Starter": { - type: "separator", - title: "Starter", - }, - "airtable-api": { - title: "Airtable API", - href: "/en/resources/integrations/productivity/airtable-api", - }, - "asana-api": { - title: "Asana API", - href: "/en/resources/integrations/productivity/asana-api", - }, - "ashby-api": { - title: "Ashby API", - href: "/en/resources/integrations/productivity/ashby-api", - }, - "box-api": { - title: "Box API", - href: "/en/resources/integrations/productivity/box-api", - }, - "calendly-api": { - title: "Calendly API", - href: "/en/resources/integrations/productivity/calendly-api", - }, - "clickup-api": { - title: "ClickUp API", - href: "/en/resources/integrations/productivity/clickup-api", - }, - "figma-api": { - title: "Figma API", - href: "/en/resources/integrations/productivity/figma-api", - }, - "luma-api": { - title: "Luma API", - href: "/en/resources/integrations/productivity/luma-api", - }, - "mailchimp-marketing-api": { - title: "Mailchimp API", - href: "/en/resources/integrations/productivity/mailchimp-marketing-api", - }, - "miro-api": { - title: "Miro API", - href: "/en/resources/integrations/productivity/miro-api", - }, - "squareup-api": { - title: "SquareUp API", - href: "/en/resources/integrations/productivity/squareup-api", - }, - "ticktick-api": { - title: "TickTick API", - href: "/en/resources/integrations/productivity/ticktick-api", - }, - "trello-api": { - title: "Trello API", - href: "/en/resources/integrations/productivity/trello-api", - }, - "xero-api": { - title: "Xero API", - href: "/en/resources/integrations/productivity/xero-api", - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/sales/[toolkitId]/_meta.tsx b/app/en/resources/integrations/sales/[toolkitId]/_meta.tsx deleted file mode 100644 index 5e2130851..000000000 --- a/app/en/resources/integrations/sales/[toolkitId]/_meta.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { MetaRecord } from "nextra"; -import { toolkitPageTheme } from "../../toolkit-page-theme"; - -const meta: MetaRecord = { - "*": { - theme: toolkitPageTheme, - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/sales/[toolkitId]/page-impl.tsx b/app/en/resources/integrations/sales/[toolkitId]/page-impl.tsx deleted file mode 100644 index 3112c162b..000000000 --- a/app/en/resources/integrations/sales/[toolkitId]/page-impl.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { createToolkitDocsPage } from "@/app/en/resources/integrations/_lib/toolkit-docs-page"; - -export const dynamicParams = false; - -const { Page, generateMetadata, generateStaticParams } = - createToolkitDocsPage("sales"); - -export { generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/sales/[toolkitId]/page.mdx b/app/en/resources/integrations/sales/[toolkitId]/page.mdx deleted file mode 100644 index ca97c319f..000000000 --- a/app/en/resources/integrations/sales/[toolkitId]/page.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "MCP server" -description: "Generated MCP server documentation." ---- - -import Page, { - dynamicParams, - generateMetadata, - generateStaticParams, -} from "./page-impl"; - -export { dynamicParams, generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/sales/_meta.tsx b/app/en/resources/integrations/sales/_meta.tsx deleted file mode 100644 index 63426d7ef..000000000 --- a/app/en/resources/integrations/sales/_meta.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "-- Optimized": { - type: "separator", - title: "Optimized", - }, - apollo: { - title: "Apollo", - href: "/en/resources/integrations/sales/apollo", - }, - attio: { - title: "Attio", - href: "/en/resources/integrations/sales/attio", - }, - hubspot: { - title: "HubSpot", - href: "/en/resources/integrations/sales/hubspot", - }, - insightly: { - title: "Insightly", - href: "/en/resources/integrations/sales/insightly", - }, - salesforce: { - title: "Salesforce", - href: "/en/resources/integrations/sales/salesforce", - }, - "-- Starter": { - type: "separator", - title: "Starter", - }, - "hubspot-automation-api": { - title: "HubSpot Automation API", - href: "/en/resources/integrations/sales/hubspot-automation-api", - }, - "hubspot-cms-api": { - title: "HubSpot CMS API", - href: "/en/resources/integrations/sales/hubspot-cms-api", - }, - "hubspot-conversations-api": { - title: "HubSpot Conversations API", - href: "/en/resources/integrations/sales/hubspot-conversations-api", - }, - "hubspot-crm-api": { - title: "HubSpot CRM API", - href: "/en/resources/integrations/sales/hubspot-crm-api", - }, - "hubspot-events-api": { - title: "HubSpot Events API", - href: "/en/resources/integrations/sales/hubspot-events-api", - }, - "hubspot-marketing-api": { - title: "HubSpot Marketing API", - href: "/en/resources/integrations/sales/hubspot-marketing-api", - }, - "hubspot-meetings-api": { - title: "HubSpot Meetings API", - href: "/en/resources/integrations/sales/hubspot-meetings-api", - }, - "hubspot-users-api": { - title: "HubSpot Users API", - href: "/en/resources/integrations/sales/hubspot-users-api", - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/search/[toolkitId]/_meta.tsx b/app/en/resources/integrations/search/[toolkitId]/_meta.tsx deleted file mode 100644 index 5e2130851..000000000 --- a/app/en/resources/integrations/search/[toolkitId]/_meta.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { MetaRecord } from "nextra"; -import { toolkitPageTheme } from "../../toolkit-page-theme"; - -const meta: MetaRecord = { - "*": { - theme: toolkitPageTheme, - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/search/[toolkitId]/page-impl.tsx b/app/en/resources/integrations/search/[toolkitId]/page-impl.tsx deleted file mode 100644 index 2d8dd42c5..000000000 --- a/app/en/resources/integrations/search/[toolkitId]/page-impl.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { createToolkitDocsPage } from "@/app/en/resources/integrations/_lib/toolkit-docs-page"; - -export const dynamicParams = false; - -const { Page, generateMetadata, generateStaticParams } = - createToolkitDocsPage("search"); - -export { generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/search/[toolkitId]/page.mdx b/app/en/resources/integrations/search/[toolkitId]/page.mdx deleted file mode 100644 index ca97c319f..000000000 --- a/app/en/resources/integrations/search/[toolkitId]/page.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "MCP server" -description: "Generated MCP server documentation." ---- - -import Page, { - dynamicParams, - generateMetadata, - generateStaticParams, -} from "./page-impl"; - -export { dynamicParams, generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/search/_meta.tsx b/app/en/resources/integrations/search/_meta.tsx deleted file mode 100644 index a06e2befe..000000000 --- a/app/en/resources/integrations/search/_meta.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "-- Optimized": { - type: "separator", - title: "Optimized", - }, - glean: { - title: "Glean", - href: "/en/resources/integrations/search/glean", - }, - google_finance: { - title: "Google Finance", - href: "/en/resources/integrations/search/google_finance", - }, - google_flights: { - title: "Google Flights", - href: "/en/resources/integrations/search/google_flights", - }, - google_hotels: { - title: "Google Hotels", - href: "/en/resources/integrations/search/google_hotels", - }, - google_jobs: { - title: "Google Jobs", - href: "/en/resources/integrations/search/google_jobs", - }, - google_maps: { - title: "Google Maps", - href: "/en/resources/integrations/search/google_maps", - }, - google_news: { - title: "Google News", - href: "/en/resources/integrations/search/google_news", - }, - google_search: { - title: "Google Search", - href: "/en/resources/integrations/search/google_search", - }, - google_shopping: { - title: "Google Shopping", - href: "/en/resources/integrations/search/google_shopping", - }, - walmart: { - title: "Walmart", - href: "/en/resources/integrations/search/walmart", - }, - youtube: { - title: "Youtube", - href: "/en/resources/integrations/search/youtube", - }, - "-- Starter": { - type: "separator", - title: "Starter", - }, - "exa-api": { - title: "Exa API", - href: "/en/resources/integrations/search/exa-api", - }, - "-- Partners": { - type: "separator", - title: "Partners", - }, - nimble: { - title: "Nimble", - href: "/en/resources/integrations/search/nimble", - }, - tavily: { - title: "Tavily", - href: "/en/resources/integrations/search/tavily", - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/social/[toolkitId]/_meta.tsx b/app/en/resources/integrations/social/[toolkitId]/_meta.tsx deleted file mode 100644 index 5e2130851..000000000 --- a/app/en/resources/integrations/social/[toolkitId]/_meta.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { MetaRecord } from "nextra"; -import { toolkitPageTheme } from "../../toolkit-page-theme"; - -const meta: MetaRecord = { - "*": { - theme: toolkitPageTheme, - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/social/[toolkitId]/page-impl.tsx b/app/en/resources/integrations/social/[toolkitId]/page-impl.tsx deleted file mode 100644 index 5b68950fd..000000000 --- a/app/en/resources/integrations/social/[toolkitId]/page-impl.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { createToolkitDocsPage } from "@/app/en/resources/integrations/_lib/toolkit-docs-page"; - -export const dynamicParams = false; - -const { Page, generateMetadata, generateStaticParams } = - createToolkitDocsPage("social"); - -export { generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/social/[toolkitId]/page.mdx b/app/en/resources/integrations/social/[toolkitId]/page.mdx deleted file mode 100644 index ca97c319f..000000000 --- a/app/en/resources/integrations/social/[toolkitId]/page.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "MCP server" -description: "Generated MCP server documentation." ---- - -import Page, { - dynamicParams, - generateMetadata, - generateStaticParams, -} from "./page-impl"; - -export { dynamicParams, generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/social/_meta.tsx b/app/en/resources/integrations/social/_meta.tsx deleted file mode 100644 index 669e6fd45..000000000 --- a/app/en/resources/integrations/social/_meta.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "-- Optimized": { - type: "separator", - title: "Optimized", - }, - "discord-bot": { - title: "Discord Bot", - href: "/en/resources/integrations/social/discord-bot", - }, - linkedin: { - title: "LinkedIn", - href: "/en/resources/integrations/social/linkedin", - }, - "microsoft-teams": { - title: "Microsoft Teams", - href: "/en/resources/integrations/social/microsoft-teams", - }, - reddit: { - title: "Reddit", - href: "/en/resources/integrations/social/reddit", - }, - slack: { - title: "Slack", - href: "/en/resources/integrations/social/slack", - }, - telegram: { - title: "Telegram", - href: "/en/resources/integrations/social/telegram", - }, - x: { - title: "X", - href: "/en/resources/integrations/social/x", - }, - zoom: { - title: "Zoom", - href: "/en/resources/integrations/social/zoom", - }, - "-- Starter": { - type: "separator", - title: "Starter", - }, - "slack-api": { - title: "Slack API", - href: "/en/resources/integrations/social/slack-api", - }, -}; - -export default meta; diff --git a/app/global.css b/app/global.css new file mode 100644 index 000000000..a722083f6 --- /dev/null +++ b/app/global.css @@ -0,0 +1,1004 @@ +@import "tailwindcss"; +@import "fumadocs-ui/css/neutral.css"; +@import "fumadocs-ui/css/preset.css"; +@import "@arcadeai/design-system/assets/tokens.css"; +@source "../node_modules/fumadocs-ui/dist/**/*.js"; +@source "../node_modules/@arcadeai/design-system/dist"; +@plugin "tailwindcss-animate"; + +/* The `dark` variant is defined by fumadocs-ui/css/preset.css (`.dark` class). */ + +@layer base { + body { + font-family: var(--font-sans); + } +} + +/* + * Brand accent — the design-system primary, adapted for contrast on the + * surface that follows the active theme. Use this token (`text-brand-accent`, + * `bg-brand-accent/10`, `border-brand-accent`, etc.) anywhere the brand color + * appears as text / icon / border on a theme-following surface. + * + * The raw `--primary` (and `text-primary`) is a very light lime designed to + * sit under dark foreground content (e.g. on `bg-primary` buttons). It's not + * readable as text/icons on a white page background. This token mixes primary + * with black in light mode so it stays legible there, and falls back to full + * primary in dark mode where the bright lime reads cleanly on the dark surface. + * + * Keep raw `text-primary` only where the local surface is dark in both modes + * (e.g. cards with a fixed dark background) — there the lime needs no shifting. + */ +@theme inline { + --color-brand-accent: var(--brand-accent); +} + +:root { + /* `oklch(from ... L c h)` keeps the chroma and hue of primary while pinning + lightness to a readable level — gives a saturated dark green instead of + the muted olive `color-mix(... black)` would produce. */ + --brand-accent: oklch(from var(--primary) 0.45 c h); + + /* + * Map Fumadocs' primary color to the design-system --primary token so the + * sidebar active state, TOC highlight and accent links follow the brand. + * (Replaces the Nextra `--x-color-primary-*` scale.) + */ + --color-fd-primary: var(--brand-accent); + --color-fd-ring: var(--brand-accent); +} + +.dark { + --brand-accent: var(--primary); + --color-fd-primary: var(--primary); + --color-fd-ring: var(--primary); +} + +/* + * Docs chrome (navbar, sidebar, table of contents, page shell, footer band). + * + * The chrome components in components/chrome/ are a port of the previous docs + * theme and keep its measurements: a 64px navbar, 256px sidebar and table of + * contents columns, and a 90rem content width. The variables and helper + * classes below are the ones that theme defined. + */ +:root { + --nextra-navbar-height: 64px; + --nextra-content-width: 90rem; + --nextra-bg: 255, 255, 255; + + /* + * Primary scale derived from the design-system --primary token. Light mode: + * lower shades fade toward white, higher shades toward black so they stay + * readable as text. Dark mode: lower shades fade toward black, higher shades + * stay near full primary. + */ + --x-color-primary-50: color-mix(in oklch, var(--primary) 12%, white); + --x-color-primary-100: color-mix(in oklch, var(--primary) 22%, white); + --x-color-primary-200: color-mix(in oklch, var(--primary) 40%, white); + --x-color-primary-300: color-mix(in oklch, var(--primary) 60%, white); + --x-color-primary-400: color-mix(in oklch, var(--primary) 80%, white); + --x-color-primary-500: var(--primary); + --x-color-primary-600: color-mix(in oklch, var(--primary) 70%, black); + --x-color-primary-700: color-mix(in oklch, var(--primary) 55%, black); + --x-color-primary-800: color-mix(in oklch, var(--primary) 45%, black); +} + +.dark { + --nextra-bg: 10, 10, 10; + --x-color-primary-50: color-mix(in oklch, var(--primary) 12%, black); + --x-color-primary-100: color-mix(in oklch, var(--primary) 22%, black); + --x-color-primary-200: color-mix(in oklch, var(--primary) 35%, black); + --x-color-primary-300: color-mix(in oklch, var(--primary) 55%, black); + --x-color-primary-400: var(--primary); + --x-color-primary-500: var(--primary); + --x-color-primary-600: var(--primary); + --x-color-primary-700: color-mix(in oklch, var(--primary) 85%, white); + --x-color-primary-800: color-mix(in oklch, var(--primary) 70%, white); +} + +@theme inline { + --color-nextra-bg: rgb(var(--nextra-bg)); + --color-primary-50: var(--x-color-primary-50); + --color-primary-100: var(--x-color-primary-100); + --color-primary-200: var(--x-color-primary-200); + --color-primary-300: var(--x-color-primary-300); + --color-primary-400: var(--x-color-primary-400); + --color-primary-500: var(--x-color-primary-500); + --color-primary-600: var(--x-color-primary-600); + --color-primary-700: var(--x-color-primary-700); + --color-primary-800: var(--x-color-primary-800); +} + +@layer base { + html { + font-feature-settings: + "rlig" 1, + "calt" 1, + "ss01" 1; + color: #000; + background: rgb(var(--nextra-bg)); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-tap-highlight-color: transparent; + } + + html.dark { + color: #fff; + } + + body { + color: inherit; + background: transparent; + } +} + +@utility nextra-focus { + @apply ring-2 ring-primary-200 ring-offset-1 ring-offset-primary-300 dark:ring-primary-800; + + &:focus-visible { + outline-style: none; + } +} + +/* Nextra had no default border colour (Tailwind's `currentColor`); Fumadocs + sets one on `*`. In the base layer so colour utilities still win. */ +@layer base { + .nextra-content *, + .nextra-content ::before, + .nextra-content ::after { + border-color: currentColor; + } +} + +/* GFM task lists */ +.nextra-content .contains-task-list { + margin-top: 1.25em; + + input[type="checkbox"] { + margin-inline-end: 0.25rem; + } +} + +.nextra-border { + border-color: var(--color-gray-200); + + @media (prefers-contrast: more) { + border-color: var(--color-neutral-400) !important; + } + + &:where(.dark *) { + border-color: var(--color-neutral-800); + } +} + +.nextra-scrollbar { + scrollbar-width: thin; + scrollbar-gutter: stable; +} + +/* Doubled selector: must win over later `.nextra-scrollbar` rules. */ +.no-scrollbar, +.nextra-scrollbar.no-scrollbar { + scrollbar-width: none; + -ms-overflow-style: none; + + &::-webkit-scrollbar { + display: none; + } +} + +.nextra-mask { + mask-image: + linear-gradient(to bottom, transparent, #000 20px, transparent 100%), + linear-gradient(to top, transparent, #000 20px, transparent 100%); + + @media (prefers-contrast: more) { + mask-image: none; + } +} + +.nextra-breadcrumb { + margin-bottom: 1.5rem; +} + +/* "Skip to Content" is only visible while it has keyboard focus. */ +.nextra-skip-nav:not(:focus-visible) { + @apply sr-only; +} + +.nextra-skip-nav:focus-visible { + @apply nextra-focus fixed z-50 mx-4 my-3 rounded-[8px] border border-current bg-nextra-bg px-3 py-2 font-bold text-sm; +} + +.nextra-hamburger svg { + --animation: transform 0.15s cubic-bezier(0.25, 1, 0.5, 1); + + g, + path { + @media (prefers-reduced-motion: reduce) { + transition-property: none !important; + } + } + + g { + transform-origin: center; + transition: var(--animation); + } + + path { + opacity: 1; + transition: + var(--animation) 0.15s, + opacity 0.15s ease 0.15s; + } + + &.open { + path { + transition: + var(--animation), + opacity 0s ease 0.15s; + } + + & > path { + opacity: 0; + } + + g { + transition: var(--animation) 0.15s; + + &:nth-of-type(1) { + transform: rotate(45deg); + + path { + transform: translate3d(0, 6px, 0); + } + } + + &:nth-of-type(2) { + transform: rotate(-45deg); + + path { + transform: translate3d(0, -6px, 0); + } + } + } + } +} + +@keyframes fade-in { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +@keyframes fade-in2 { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +/* Position search next to logo */ +.nextra-navbar nav > a[aria-label="Home page"] { + order: -2; + margin-inline-end: 3.5rem !important; +} + +.nextra-navbar nav > div:has(.algolia-search-button) { + order: -1; + margin-inline: 1rem auto; +} + +@media (max-width: 1024px) { + .nextra-navbar nav > a[aria-label="Home page"] { + margin-inline-end: auto !important; + } +} + +.text-text-color { + color: var(--color-foreground); +} + +.border-neutral-dark-medium { + border-color: oklch(from var(--border) l c h / 0.3); +} + +.border-neutral-dark-high { + border-color: oklch(from var(--border) l c h / 0.8); +} + +.border-neutral-dark-low { + border-color: oklch(from var(--border) l c h / 0.2); +} + +.bg-neutral-dark { + background-color: oklch(from var(--card) l c h / 0.4); +} + +.bg-neutral-dark-medium { + background-color: oklch(from var(--muted) l c h / 0.1); +} + +.hover\:bg-neutral-dark-high:hover { + background-color: oklch(from var(--muted) l c h / 0.6); +} + +.bg-neutral-dark-opacity { + background-color: oklch(from var(--muted) l c h / 0.05); +} + +.text-neutral-light-high { + color: var(--foreground); +} + +code { + text-wrap: pretty; + white-space: pre-wrap; +} + +/* Hide the "Edit on GitHub" link on toolkit pages (they have their own) */ +html[data-page-kind="toolkit"] { + a[href*="github.com/ArcadeAI/docs/tree/main/"], + a[href*="github.com/ArcadeAI/docs/blob/main/"] { + display: none !important; + } +} + +pre code { + font-family: + ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; +} + +/* Toolkit pages: Improve code readability in light mode */ +html[data-page-kind="toolkit"].light pre, +html[data-page-kind="toolkit"]:not(.dark) pre { + color: #0f172a; + background-color: #f8fafc; +} + +html[data-page-kind="toolkit"].light pre code, +html[data-page-kind="toolkit"]:not(.dark) pre code { + color: inherit; +} + +/* + * Toolkit pages: callout colors for better readability. Nextra's + * `.nextra-callout.x:bg-*` selectors are replaced by Fumadocs' callout + * `data-type` attribute (warn | info | success | idea | error). + */ +html[data-page-kind="toolkit"] .fd-callout, +html[data-page-kind="toolkit"] [data-callout] { + border-width: 1px; + border-radius: 0.75rem; +} + +html[data-page-kind="toolkit"] [data-callout][data-type="warn"] { + color: rgb(113 63 18) !important; + background-color: rgb(254 252 232) !important; + border-color: rgb(234 179 8) !important; +} + +html.dark[data-page-kind="toolkit"] [data-callout][data-type="warn"] { + color: rgb(253 224 71) !important; + background-color: rgb(113 63 18 / 0.15) !important; + border-color: rgb(234 179 8 / 0.4) !important; +} + +html[data-page-kind="toolkit"] [data-callout][data-type="info"] { + color: rgb(29 78 216) !important; + background-color: rgb(239 246 255) !important; + border-color: rgb(59 130 246) !important; +} + +html.dark[data-page-kind="toolkit"] [data-callout][data-type="info"] { + color: rgb(147 197 253) !important; + background-color: rgb(30 58 138 / 0.15) !important; + border-color: rgb(59 130 246 / 0.4) !important; +} + +html[data-page-kind="toolkit"] [data-callout][data-type="success"], +html[data-page-kind="toolkit"] [data-callout][data-type="idea"] { + color: rgb(21 128 61) !important; + background-color: rgb(240 253 244) !important; + border-color: rgb(34 197 94) !important; +} + +html.dark[data-page-kind="toolkit"] [data-callout][data-type="success"], +html.dark[data-page-kind="toolkit"] [data-callout][data-type="idea"] { + color: rgb(134 239 172) !important; + background-color: rgb(20 83 45 / 0.15) !important; + border-color: rgb(34 197 94 / 0.4) !important; +} + +/* Algolia search hit highlight — brand color */ +.ais-Highlight-highlighted, +.ais-Snippet-highlighted, +[data-search-dialog] mark { + font-style: normal; + font-weight: 600; + color: var(--primary); + background: color-mix(in oklch, var(--primary) 15%, transparent); + border-radius: 2px; +} + +.dark .ais-Highlight-highlighted, +.dark .ais-Snippet-highlighted, +.dark [data-search-dialog] mark { + background: color-mix(in oklch, var(--primary) 20%, transparent); +} + +/* Code highlight colors: green instead of the default accent */ +[data-highlighted-line], +.shiki .highlighted { + --tw-shadow: 2px 0 #22c55e inset !important; + background-color: color-mix(in oklab, #22c55e 20%, transparent) !important; + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow) !important; +} + +/* Remove top margin from nested lists in GuideOverview component */ +.guide-overview ul ul { + margin-top: 0; +} + +@keyframes tool-name-marquee { + 0% { + transform: translateX(0); + } + 100% { + transform: translateX(-100%); + } +} + +/* + * Anchor scroll offset. Set it unconditionally, with room to breathe, so a + * heading never lands hidden under the sticky navbar. Headings and sections + * should not add their own `scroll-mt-*`; it stacks on top of this. + */ +html:root { + scroll-padding-top: calc(var(--nextra-navbar-height) + 1.5rem); +} + +/* + * The smooth anchor scrolling on this site came from zenscroll (bundled with + * swagger-ui-react), which app/layout.tsx stands down because it ignored the + * offset above. Ask the browser for the same easing instead — native + * scrolling honors scroll-padding-top. + */ +html { + scroll-behavior: smooth; +} + +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + } +} + +/* ========================================================================== + * pages parity — START + * Styles for the component-built pages (toolkit pages, integrations catalog, + * home, contact, surveys). Edit only between START and END. + * ========================================================================== */ + +/* + * `dark:` variant specificity. + * + * The design-system tokens define the variant as `&:is(.dark *)`, which adds + * a class to the specificity of every `dark:` utility. The docs redefine it + * as `&:where(.dark *)`, which adds nothing, and the design-system components + * are tuned for that: a state variant such as `aria-expanded:bg-muted` or + * `hover:bg-muted` has to beat a plain `dark:bg-input/30` on the same element. + * With the `:is()` form the dark utility wins instead, so an open "Hide + * details" button, for example, loses its filled background in dark mode. + * + * The last definition wins, so this must stay after the tokens import. It + * applies to every `dark:` utility on the site. + */ +@variant dark (&:where(.dark *)); + +/* + * Typography for toolkit pages. + * + * The toolkit summary and documentation chunks are styled with + * @tailwindcss/typography (`prose prose-sm dark:prose-invert`). Fumadocs' + * preset registers its own, different typography under the same `prose` + * names, so the plugin is registered here under `tk-prose`: the classes are + * `tk-prose tk-prose-sm dark:tk-prose-invert`, the rules are the plugin's. + */ +@plugin "@tailwindcss/typography" { + className: tk-prose; +} + +/* + * Default border colour for component-built pages. + * + * These components were written against Tailwind's preflight, where a border + * without a colour utility is drawn in the text colour. Several of their + * colour utilities do not exist (`divide-neutral-dark-high/30`, + * `border-neutral-dark-high/20`), so those dividers and section borders are + * meant to be drawn in the text colour. Fumadocs' preset changes the default + * to its own border token, which makes them near-invisible. `.preflight-borders` + * restores the preflight default for a subtree. MDX content rendered inside + * `.tk-prose` (callouts, tabs, code blocks) is left alone: those components + * rely on the Fumadocs default. + * + * The rule is in the components layer so colour utilities still win. + */ +@layer components { + .preflight-borders, + .preflight-borders *:not(.tk-prose *), + .preflight-borders *:not(.tk-prose *)::before, + .preflight-borders *:not(.tk-prose *)::after { + border-color: currentcolor; + } +} + +/* pages parity — END */ + +/* ========================================================================== + * content parity — BEGIN + * Owner: MDX content components (components/mdx/**, mdx-components.tsx). + * The named classes of the Nextra docs theme (nextra-theme-docs 4.6 + * style.css), copied so the ported element renderers look exactly as they do + * on the Nextra-built production site. Unlayered on purpose: in Nextra these + * rules also sat outside the Tailwind layers and beat utility classes. + * ========================================================================== */ + +/* Nextra's primary scale, derived from the design-system primary exactly as + the production globals.css derived it. */ +:root { + --x-color-primary-50: color-mix(in oklch, var(--primary) 12%, white); + --x-color-primary-100: color-mix(in oklch, var(--primary) 22%, white); + --x-color-primary-200: color-mix(in oklch, var(--primary) 40%, white); + --x-color-primary-300: color-mix(in oklch, var(--primary) 60%, white); + --x-color-primary-400: color-mix(in oklch, var(--primary) 80%, white); + --x-color-primary-500: var(--primary); + --x-color-primary-600: color-mix(in oklch, var(--primary) 70%, black); + --x-color-primary-700: color-mix(in oklch, var(--primary) 55%, black); + --x-color-primary-800: color-mix(in oklch, var(--primary) 45%, black); + --nextra-content-bg: #fff; +} + +.dark { + --x-color-primary-50: color-mix(in oklch, var(--primary) 12%, black); + --x-color-primary-100: color-mix(in oklch, var(--primary) 22%, black); + --x-color-primary-200: color-mix(in oklch, var(--primary) 35%, black); + --x-color-primary-300: color-mix(in oklch, var(--primary) 55%, black); + --x-color-primary-400: var(--primary); + --x-color-primary-500: var(--primary); + --x-color-primary-600: var(--primary); + --x-color-primary-700: color-mix(in oklch, var(--primary) 85%, white); + --x-color-primary-800: color-mix(in oklch, var(--primary) 70%, white); + --nextra-content-bg: #0a0a0a; +} + +@utility nextra-focus { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + --tw-ring-color: var(--x-color-primary-200); + --tw-ring-offset-width: 1px; + --tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 + var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-offset-color: var(--x-color-primary-300); + + &:focus-visible { + outline-style: none; + } + + /* `:is` (not `:where`) so this outranks the second `nextra-focus` + definition earlier in this file, which has no dark offset colour. */ + &:is(.dark *) { + --tw-ring-color: var(--x-color-primary-800); + --tw-ring-offset-color: var(--x-color-primary-700); + } +} + +/* Nextra set these on <html>; scoped here to the MDX body. */ +.nextra-content { + font-size: 1rem; + line-height: 1.5; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-feature-settings: + "rlig" 1, + "calt" 1, + "ss01" 1; +} + +.nextra-border { + border-color: var(--color-gray-200); + + @media (prefers-contrast: more) { + border-color: var(--color-neutral-400) !important; + } + + &:where(.dark *) { + border-color: var(--color-neutral-800); + } +} + +.nextra-scrollbar { + scrollbar-width: thin; + scrollbar-gutter: stable; +} + +/* Heading permalink: the "#" shown on hover, focus and :target. */ +:hover > .subheading-anchor { + opacity: 100%; +} + +.subheading-anchor { + margin-inline-start: 0.25rem; + opacity: 0%; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-property: + color, background-color, border-color, outline-color, text-decoration-color, + fill, stroke, opacity, box-shadow, transform, translate, scale, rotate, + filter, backdrop-filter; + + :target > &, + &:focus { + opacity: 100%; + } + + &::after { + padding-inline: 0.25rem; + color: var(--color-gray-600); + content: "#"; + } + + &:where(.dark *)::after { + color: var(--color-neutral-400); + } + + :target > &::after { + color: var(--color-gray-600); + } + + :target > &:where(.dark *)::after { + color: var(--color-neutral-400); + } +} + +@keyframes fade-in { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +/* Code: token colours, inline code, line grid, numbers, highlights. */ +.nextra-code span { + color: var(--shiki-light); + background-color: var(--shiki-light-bg); + + &:where(.dark *) { + color: var(--shiki-dark); + background-color: var(--shiki-dark-bg); + } +} + +code.nextra-code { + box-decoration-break: slice; + font-size: 0.875rem; + line-height: var(--tw-leading, calc(1.25 / 0.875)); + font-feature-settings: + "rlig" 1, + "calt" 1, + "ss01" 1; + + :not(pre) > & { + padding: 0.12em 0.25em; + overflow-wrap: break-word; + font-size: 0.9em; + background-color: color-mix(in oklab, #000 3%, transparent); + border: 1px solid color-mix(in oklab, #000 4%, transparent); + border-radius: 0.375em; + + &:where(.dark *) { + background-color: color-mix(in oklab, #fff 10%, transparent); + border-color: color-mix(in oklab, #fff 10%, transparent); + } + } +} + +pre code.nextra-code { + display: grid; + counter-reset: line; + + &[data-line-numbers] > span { + padding-left: 0.5rem; + + &::before { + display: inline-block; + min-width: 2.6rem; + padding-right: 1rem; + color: var(--color-gray-600); + text-align: right; + content: counter(line); + counter-increment: line; + } + + &:where(.dark *)::before { + color: var(--color-gray-400); + } + } + + & > span { + padding-inline: 1rem; + + &[data-highlighted-line] { + --tw-shadow: 2px 0 var(--tw-shadow-color, currentColor) inset; + color: color-mix( + in oklab, + var(--x-color-primary-600) 80%, + transparent + ) !important; + background-color: color-mix( + in oklab, + var(--x-color-primary-600) 10%, + transparent + ); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + + &:where(.dark *) { + background-color: color-mix( + in oklab, + var(--x-color-primary-600) 20%, + transparent + ); + } + } + } + + html[data-nextra-word-wrap] & { + @media (width < 48rem) { + white-space: pre-wrap; + } + } +} + +.nextra-copy-icon { + animation: fade-in 0.3s ease forwards; +} + +/* The hidden tab-label heading must not push the panel's first block down. */ +[role="tabpanel"][data-selected] > h3[style^="visibility:"] + * { + margin-top: 0; +} + +/* Steps: every h3 inside is numbered by a CSS counter (h4–h6 are not, as in + the production globals.css). */ +.nextra-steps { + h2, + h3:not([style^="visibility:"]), + h4, + h5, + h6 { + padding-bottom: 0; + margin-top: 2rem; + font-size: 1.5rem; + font-weight: 600; + line-height: var(--tw-leading, calc(2 / 1.5)); + letter-spacing: -0.025em; + counter-increment: var(--counter-id); + border-style: solid; + border-width: 0; + + &::before { + position: absolute; + width: 33px; + height: 33px; + margin-inline-start: -41px; + font-size: 1rem; + font-weight: 400; + line-height: var(--tw-leading, 1.5); + color: var(--color-neutral-800); + text-align: center; + text-indent: -1px; + content: counter(var(--counter-id)); + background-color: var(--color-gray-200); + border: 4px solid var(--nextra-content-bg); + border-radius: calc(infinity * 1px); + } + + &:where(.dark *)::before { + color: var(--color-neutral-300); + background-color: var(--color-neutral-800); + } + } + + h4, + h5, + h6 { + counter-increment: none !important; + + &::before { + display: none !important; + content: none !important; + } + } +} + +/* Cards */ +.nextra-cards { + grid-template-columns: repeat( + auto-fill, + minmax(max(250px, calc((100% - 1rem * 2) / var(--rows))), 1fr) + ); +} + +.nextra-card { + img { + user-select: none; + } + + & > span > svg { + flex-shrink: 0; + width: 1.5rem; + height: auto; + color: color-mix(in oklab, #000 50%, transparent); + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + transition-property: color, background-color, border-color, fill, stroke; + + &:where(.dark *) { + color: color-mix(in oklab, #fff 50%, transparent); + } + } + + &:hover > span > svg { + color: currentcolor !important; + } + + p { + margin-top: 0.5rem; + } +} + +/* Image zoom (react-medium-image-zoom), Nextra's rules. The `html` prefix + outranks the Fumadocs image-zoom stylesheet, which loads later. */ +.nextra-content [data-rmiz] { + display: inline; + position: static; +} + +html [data-rmiz-ghost] { + position: absolute; + pointer-events: none; +} + +html [data-rmiz-btn-zoom], +html [data-rmiz-btn-unzoom] { + display: inline-block; + width: 2.5rem; + height: 2.5rem; + padding: 9px; + color: #fff; + touch-action: manipulation; + appearance: none; + outline-offset: 2px; + background-color: color-mix(in oklab, #000 70%, transparent); + border-radius: calc(infinity * 1px); + + &:where(.dark *) { + color: #000; + background-color: color-mix(in oklab, #fff 70%, transparent); + } +} + +html [data-rmiz-btn-zoom] { + position: absolute; + inset: 10px 10px auto auto; + cursor: zoom-in; + + &:not(:focus):not(:active) { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip: rect(0 0 0 0); + white-space: nowrap; + pointer-events: none; + clip-path: inset(50%); + } +} + +html [data-rmiz-btn-unzoom] { + position: absolute; + inset: 20px 20px auto auto; + z-index: 1; + cursor: zoom-out; +} + +html [data-rmiz-content="found"] { + img, + svg, + [role="img"], + [data-zoom] { + cursor: zoom-in; + } +} + +html [data-rmiz-modal-overlay] { + position: absolute; + inset: 0; + transition: opacity 0.3s; +} + +html [data-rmiz-modal-overlay="hidden"] { + background: color-mix(in srgb, var(--nextra-content-bg) 80%, transparent); + opacity: 0%; +} + +html [data-rmiz-modal-overlay="visible"] { + background: color-mix(in srgb, var(--nextra-content-bg) 80%, transparent); + opacity: 100%; +} + +html [data-rmiz-modal-img] { + position: absolute; + cursor: zoom-out; + image-rendering: high-quality; + transition: transform 0.3s; + transform-origin: top left; +} + +@media (prefers-reduced-motion: reduce) { + html [data-rmiz-modal-overlay], + html [data-rmiz-modal-img] { + transition-duration: 0.01ms; + } +} + +/* content parity — END */ + +/* + * Sidebar rows are flex, with the disclosure icon beside the label. Nextra's + * word-break: break-word then splits a word when that column is a character + * short. Reserve room for the icon and wrap ordinary words at spaces. + */ +.nextra-sidebar :is(a, button)[class*="word-break"], +.nextra-mobile-nav :is(a, button)[class*="word-break"] { + display: block; + position: relative; + word-break: normal; + overflow-wrap: break-word; +} + +.nextra-sidebar :is(a, button)[class*="word-break"]:has(> svg), +.nextra-mobile-nav :is(a, button)[class*="word-break"]:has(> svg) { + padding-inline-end: 1.75rem; +} + +.nextra-sidebar :is(a, button)[class*="word-break"] > svg, +.nextra-mobile-nav :is(a, button)[class*="word-break"] > svg { + position: absolute; + inset-inline-end: 0.5rem; + top: 0.375rem; +} + +.nextra-sidebar li, +.nextra-mobile-nav li { + word-break: normal; + overflow-wrap: break-word; +} diff --git a/app/globals.css b/app/globals.css deleted file mode 100644 index e88397439..000000000 --- a/app/globals.css +++ /dev/null @@ -1,329 +0,0 @@ -@import "nextra-theme-docs/style.css"; -@import "tailwindcss"; -@import "@arcadeai/design-system/assets/tokens.css"; -@source "../node_modules/@arcadeai/design-system/dist"; -@plugin 'tailwindcss-animate'; -@plugin '@tailwindcss/typography'; - -@variant dark (&:where(.dark *)); - -@layer base { - body { - font-family: var(--font-sans); - } -} - -.nextra-breadcrumb { - margin-bottom: 1.5rem; -} - -/* - * Brand accent — the design-system primary, adapted for contrast on the - * surface that follows the active theme. Use this token (`text-brand-accent`, - * `bg-brand-accent/10`, `border-brand-accent`, etc.) anywhere the brand color - * appears as text / icon / border on a theme-following surface. - * - * The raw `--primary` (and `text-primary`) is a very light lime designed to - * sit under dark foreground content (e.g. on `bg-primary` buttons). It's not - * readable as text/icons on a white page background. This token mixes primary - * with black in light mode so it stays legible there, and falls back to full - * primary in dark mode where the bright lime reads cleanly on the dark surface. - * - * Keep raw `text-primary` only where the local surface is dark in both modes - * (e.g. cards with a fixed dark background) — there the lime needs no shifting. - */ -@theme inline { - --color-brand-accent: var(--brand-accent); -} - -:root { - /* `oklch(from ... L c h)` keeps the chroma and hue of primary while pinning - lightness to a readable level — gives a saturated dark green instead of - the muted olive `color-mix(... black)` would produce. */ - --brand-accent: oklch(from var(--primary) 0.45 c h); -} - -.dark { - --brand-accent: var(--primary); -} - -.text-text-color { - color: var(--color-foreground); -} - -.border-neutral-dark-medium { - border-color: oklch(from var(--border) l c h / 0.3); -} - -.border-neutral-dark-high { - border-color: oklch(from var(--border) l c h / 0.8); -} - -.border-neutral-dark-low { - border-color: oklch(from var(--border) l c h / 0.2); -} - -.bg-neutral-dark { - background-color: oklch(from var(--card) l c h / 0.4); -} - -.bg-neutral-dark-medium { - background-color: oklch(from var(--muted) l c h / 0.1); -} - -.hover\:bg-neutral-dark-high:hover { - background-color: oklch(from var(--muted) l c h / 0.6); -} - -.bg-neutral-dark-opacity { - background-color: oklch(from var(--muted) l c h / 0.05); -} - -.text-neutral-light-high { - color: var(--foreground); -} - -code { - text-wrap: pretty; - white-space: pre-wrap; -} - -/* Hide "Edit content in GitHub" link from Nextra on toolkit pages (we have our own) */ -html[data-page-kind="toolkit"] { - a[href*="github.com/ArcadeAI/docs/tree/main/"], - a[href*="github.com/ArcadeAI/docs/blob/main/"] { - display: none !important; - } -} - -pre code { - font-family: - ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; -} - -/* Toolkit pages: Improve code readability in light mode */ -html[data-page-kind="toolkit"].light pre, -html[data-page-kind="toolkit"]:not(.dark) pre { - background-color: #f8fafc; - color: #0f172a; -} - -html[data-page-kind="toolkit"].light pre code, -html[data-page-kind="toolkit"]:not(.dark) pre code, -html[data-page-kind="toolkit"].light .nextra-code, -html[data-page-kind="toolkit"]:not(.dark) .nextra-code { - color: inherit; -} - -/* Toolkit pages: Override Nextra callout colors for better readability */ -html[data-page-kind="toolkit"] .nextra-callout { - border-width: 1px; - border-radius: 0.75rem; -} - -/* Warning callouts - toolkit pages only */ -html[data-page-kind="toolkit"] .nextra-callout.x\:bg-yellow-50 { - background-color: rgb(254 252 232) !important; - color: rgb(113 63 18) !important; - border-color: rgb(234 179 8) !important; -} - -html.dark[data-page-kind="toolkit"] .nextra-callout.x\:bg-yellow-50 { - background-color: rgb(113 63 18 / 0.15) !important; - color: rgb(253 224 71) !important; - border-color: rgb(234 179 8 / 0.4) !important; -} - -/* Info callouts - toolkit pages only */ -html[data-page-kind="toolkit"] .nextra-callout.x\:bg-blue-100 { - background-color: rgb(239 246 255) !important; - color: rgb(29 78 216) !important; - border-color: rgb(59 130 246) !important; -} - -html.dark[data-page-kind="toolkit"] .nextra-callout.x\:bg-blue-100 { - background-color: rgb(30 58 138 / 0.15) !important; - color: rgb(147 197 253) !important; - border-color: rgb(59 130 246 / 0.4) !important; -} - -/* Success/default callouts - toolkit pages only */ -html[data-page-kind="toolkit"] .nextra-callout.x\:bg-green-100 { - background-color: rgb(240 253 244) !important; - color: rgb(21 128 61) !important; - border-color: rgb(34 197 94) !important; -} - -html.dark[data-page-kind="toolkit"] .nextra-callout.x\:bg-green-100 { - background-color: rgb(20 83 45 / 0.15) !important; - color: rgb(134 239 172) !important; - border-color: rgb(34 197 94 / 0.4) !important; -} - -/* Position search next to logo */ -nav > a[aria-label="Home page"] { - order: -2; - margin-inline-end: 3.5rem !important; -} - -nav > div:has(.algolia-search-button) { - order: -1; - margin-inline-start: 1rem; - margin-inline-end: auto; -} - -@media (max-width: 1024px) { - nav > a[aria-label="Home page"] { - margin-inline-end: auto !important; - } -} - -/* Algolia search hit highlight — brand red */ -.ais-Highlight-highlighted, -.ais-Snippet-highlighted { - background: color-mix(in oklch, var(--primary) 15%, transparent); - color: var(--primary); - border-radius: 2px; - font-style: normal; - font-weight: 600; -} - -.dark .ais-Highlight-highlighted, -.dark .ais-Snippet-highlighted { - background: color-mix(in oklch, var(--primary) 20%, transparent); -} - -/* Override Nextra code highlight colors to use green instead of red */ -[data-highlighted-line] { - background-color: color-mix(in oklab, #22c55e 20%, transparent) !important; - --tw-shadow: 2px 0 #22c55e inset !important; - box-shadow: - var(--tw-inset-shadow), var(--tw-inset-ring-shadow), - var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow) !important; -} - -/* Remove top margin from nested lists in GuideOverview component */ -.guide-overview ul ul { - margin-top: 0; -} - -@keyframes tool-name-marquee { - 0% { - transform: translateX(0); - } - 100% { - transform: translateX(-100%); - } -} - -/* Only number h3 (###) in Steps component, not smaller nested headings */ -.nextra-steps h4, -.nextra-steps h5, -.nextra-steps h6 { - counter-increment: none !important; -} - -.nextra-steps h4::before, -.nextra-steps h5::before, -.nextra-steps h6::before { - content: none !important; - display: none !important; -} - -/* - * Map Nextra's primary color scale to the design-system --primary token. - * Nextra derives its primary scale (50–800) from a single HSL set in <Head color={}>, - * so the sidebar active state, TOC highlight, and accent links still read pink even - * after the design system shipped a new --primary. Re-deriving the scale from - * var(--primary) keeps everything in sync without duplicating the token. - * - * Light mode: lower shades fade toward white (subtle tints); higher shades fade - * toward black so they remain readable as text on light backgrounds. - * Dark mode: lower shades fade toward black; higher shades stay near full primary - * so accent text is bright on dark surfaces. - */ -:root { - --x-color-primary-50: color-mix(in oklch, var(--primary) 12%, white); - --x-color-primary-100: color-mix(in oklch, var(--primary) 22%, white); - --x-color-primary-200: color-mix(in oklch, var(--primary) 40%, white); - --x-color-primary-300: color-mix(in oklch, var(--primary) 60%, white); - --x-color-primary-400: color-mix(in oklch, var(--primary) 80%, white); - --x-color-primary-500: var(--primary); - --x-color-primary-600: color-mix(in oklch, var(--primary) 70%, black); - --x-color-primary-700: color-mix(in oklch, var(--primary) 55%, black); - --x-color-primary-800: color-mix(in oklch, var(--primary) 45%, black); -} - -.dark { - --x-color-primary-50: color-mix(in oklch, var(--primary) 12%, black); - --x-color-primary-100: color-mix(in oklch, var(--primary) 22%, black); - --x-color-primary-200: color-mix(in oklch, var(--primary) 35%, black); - --x-color-primary-300: color-mix(in oklch, var(--primary) 55%, black); - --x-color-primary-400: var(--primary); - --x-color-primary-500: var(--primary); - --x-color-primary-600: var(--primary); - --x-color-primary-700: color-mix(in oklch, var(--primary) 85%, white); - --x-color-primary-800: color-mix(in oklch, var(--primary) 70%, white); -} - -/* - * Anchor scroll offset. Nextra sets `scroll-padding-top` to exactly the navbar - * height, so a heading lands flush against the navbar's bottom edge with no - * gap, and it scopes that to `html:not(:has(*:focus))` — so when a click leaves - * focus on the page the offset disappears entirely and the heading ends up - * hidden behind the navbar. Set it unconditionally, with room to breathe. - * - * `html:root` matches Nextra's specificity and wins on source order. Headings - * and sections should not add their own `scroll-mt-*`; it stacks on top of this - * and leaves a large empty band above the target. - */ -html:root { - scroll-padding-top: calc(var(--nextra-navbar-height) + 1.5rem); -} - -/* - * The smooth anchor scrolling on this site came from zenscroll (bundled with - * swagger-ui-react), which app/layout.tsx now stands down because it ignored - * the offset above. Ask the browser for the same easing instead — native - * scrolling honors scroll-padding-top. - */ -html { - scroll-behavior: smooth; -} - -@media (prefers-reduced-motion: reduce) { - html { - scroll-behavior: auto; - } -} - -/* - * Sidebar rows are flex, with the disclosure icon beside the label. Nextra's - * word-break: break-word then splits a word when that column is a character - * short. Reserve room for the icon and wrap ordinary words at spaces. - */ -.nextra-sidebar :is(a, button)[class*="word-break"], -.nextra-mobile-nav :is(a, button)[class*="word-break"] { - display: block; - position: relative; - word-break: normal; - overflow-wrap: break-word; -} - -.nextra-sidebar :is(a, button)[class*="word-break"]:has(> svg), -.nextra-mobile-nav :is(a, button)[class*="word-break"]:has(> svg) { - padding-inline-end: 1.75rem; -} - -.nextra-sidebar :is(a, button)[class*="word-break"] > svg, -.nextra-mobile-nav :is(a, button)[class*="word-break"] > svg { - position: absolute; - inset-inline-end: 0.5rem; - top: 0.375rem; -} - -.nextra-sidebar li, -.nextra-mobile-nav li { - word-break: normal; - overflow-wrap: break-word; -} diff --git a/app/layout.tsx b/app/layout.tsx index bb9aab73f..cc4cbee78 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,96 +1,69 @@ -import { getDictionary } from "@/_dictionaries/get-dictionary"; -import { AlgoliaSearch } from "@/app/_components/algolia-search"; -import { SignupLink } from "@/app/_components/analytics"; -import CustomLayout from "@/app/_components/custom-layout"; -import { getDashboardUrl } from "@/app/_components/dashboard-link"; -import { Footer } from "@/app/_components/footer"; -import { Logo } from "@/app/_components/logo"; -import NavBarButton from "@/app/_components/nav-bar-button"; -import "@/app/globals.css"; -import { Discord, Github } from "@arcadeai/design-system"; +import "./global.css"; import { GoogleTagManager } from "@next/third-parties/google"; -import Link from "next/link"; +import type { Metadata, Viewport } from "next"; import Script from "next/script"; -import { Head } from "nextra/components"; -import { getPageMap } from "nextra/page-map"; -import { - LastUpdated, - Layout, - Navbar, - Footer as NextraFooter, -} from "nextra-theme-docs"; +import type React from "react"; +import CustomLayout from "@/components/custom-layout"; +import { AppRootProvider } from "@/components/root-provider"; -/** - * Nextra's active-state detection only checks `item.route`, never `item.href`. - * Toolkit sidebar entries use `href` (required so Nextra doesn't fail validation - * for keys with no matching page on disk), but get no `route`. Copy `href` → - * `route` so the sidebar highlights correctly when you're on a toolkit page. - */ -function addRoutesToHrefItems(items: object[]): object[] { - return items.map((item) => { - const i = item as Record<string, unknown>; - const withRoute = i.href && !i.route ? { ...i, route: i.href } : i; - if (Array.isArray(withRoute.children)) { - return { - ...withRoute, - children: addRoutesToHrefItems(withRoute.children as object[]), - }; - } - return withRoute; - }); -} +export const metadata: Metadata = { + appleWebApp: { + title: "Arcade Documentation", + }, + description: + "Arcade is the enterprise-ready actions runtime for AI agents — enforce your security policies on every action, execute reliably across any system, and govern agents centrally in production.", + icons: { + apple: { sizes: "180x180", url: "/apple-touch-icon.png" }, + icon: [ + { sizes: "16x16", type: "image/png", url: "/favicon-16x16.png" }, + { sizes: "32x32", type: "image/png", url: "/favicon-32x32.png" }, + { url: "/favicon.ico" }, + ], + other: [ + { color: "#5bbad5", rel: "mask-icon", url: "/safari-pinned-tab.svg" }, + ], + }, + manifest: "/site.webmanifest", + metadataBase: new URL("https://docs.arcade.dev"), + openGraph: { + images: [ + { + alt: "Arcade", + height: 630, + url: "/images/logo/arcade_og-logo.jpg", + width: 1200, + }, + ], + siteName: "Arcade Docs", + type: "website", + url: "https://docs.arcade.dev", + }, + other: { + "apple-mobile-web-app-title": "Arcade Documentation", + "twitter:site:domain": "docs.arcade.dev", + "twitter:url": "https://docs.arcade.dev", + }, + title: { + default: "Arcade Docs", + template: "%s | Arcade Docs", + }, + twitter: { + card: "summary_large_image", + images: ["/images/logo/arcade_og-logo.jpg"], + site: "@TryArcade", + }, +}; -export function generateMetadata() { - return { - title: { - default: "Arcade Docs", - template: "%s | Arcade Docs", - }, - description: - "Arcade is the enterprise-ready actions runtime for AI agents — enforce your security policies on every action, execute reliably across any system, and govern agents centrally in production.", - metadataBase: new URL("https://docs.arcade.dev"), - manifest: "/site.webmanifest", - icons: { - icon: [ - { url: "/favicon-16x16.png", sizes: "16x16", type: "image/png" }, - { url: "/favicon-32x32.png", sizes: "32x32", type: "image/png" }, - { url: "/favicon.ico" }, - ], - apple: { url: "/apple-touch-icon.png", sizes: "180x180" }, - other: [ - { rel: "mask-icon", url: "/safari-pinned-tab.svg", color: "#5bbad5" }, - ], - }, - openGraph: { - type: "website", - url: "https://docs.arcade.dev", - siteName: "Arcade Docs", - images: [ - { - url: "/images/logo/arcade_og-logo.jpg", - width: 1200, - height: 630, - alt: "Arcade", - }, - ], - }, - twitter: { - card: "summary_large_image", - site: "@TryArcade", - images: ["/images/logo/arcade_og-logo.jpg"], - }, - appleWebApp: { - title: "Arcade Documentation", - }, - other: { - "apple-mobile-web-app-title": "Arcade Documentation", - "twitter:url": "https://docs.arcade.dev", - "twitter:site:domain": "docs.arcade.dev", - }, - }; -} +export const viewport: Viewport = { + initialScale: 1, + themeColor: [ + { color: "rgb(255,255,255)", media: "(prefers-color-scheme: light)" }, + { color: "rgb(10,10,10)", media: "(prefers-color-scheme: dark)" }, + ], + width: "device-width", +}; -export default async function RootLayout({ +export default function RootLayout({ children, }: { children: React.ReactNode; @@ -98,10 +71,6 @@ export default async function RootLayout({ // Docs are English-only. proxy.ts redirects legacy /es and /pt-BR URLs to /en. const lang = "en"; - const dictionary = await getDictionary(lang); - const rawPageMap = await getPageMap(`/${lang}`); - const pageMap = addRoutesToHrefItems(rawPageMap) as typeof rawPageMap; - return ( <html data-scroll-behavior="smooth" @@ -109,22 +78,12 @@ export default async function RootLayout({ lang={lang} suppressHydrationWarning > - <Head - backgroundColor={{ - dark: "#0a0a0a", - light: "#ffffff", - }} - color={{ - hue: 347, - saturation: 100, - lightness: 50, - }} - > + <head> <meta content={lang} httpEquiv="Content-Language" /> <meta content="width=device-width, initial-scale=1" name="viewport" /> <link href="https://www.googletagmanager.com" rel="preconnect" /> <link href="https://www.googletagmanager.com" rel="dns-prefetch" /> - </Head> + </head> <body> {/* swagger-ui-react bundles zenscroll, which installs a document-wide @@ -140,70 +99,14 @@ export default async function RootLayout({ <Script id="disable-zenscroll-anchors" strategy="beforeInteractive"> {"window.noZensmooth = true;"} </Script> - <Layout - copyPageButton={true} - docsRepositoryBase="https://github.com/ArcadeAI/docs/tree/main/" - editLink={dictionary.editPage} - feedback={{ - content: false, - }} - footer={ - <NextraFooter> - <Footer /> - </NextraFooter> - } - i18n={[{ locale: "en", name: "English" }]} - lastUpdated={<LastUpdated>{dictionary.lastUpdated}</LastUpdated>} - navbar={ - <Navbar - chatIcon={ - <Discord className="size-6 transition-colors duration-150 ease-in-out [&>path]:fill-current" /> - } - chatLink="https://discord.gg/GUZEMpEZ9p" - logo={<Logo />} - logoLink={"https://arcade.dev/"} - projectIcon={ - <Github className="size-5.5 transition-colors duration-150 ease-in-out [&>path]:fill-current" /> - } - projectLink="https://github.com/ArcadeAI/arcade-mcp" - > - <SignupLink linkLocation="docs:navbar" utmMedium="navbar"> - <NavBarButton - hideOnPath={[ - "/build/create-tools/add-tools-to-arcade-catalog", - ]} - text="Sign Up" - /> - </SignupLink> - <Link href={getDashboardUrl("playground/chat")}> - <NavBarButton text="Dashboard" variant="outline" /> - </Link> - </Navbar> - } - nextThemes={{ defaultTheme: "dark" }} - pageMap={pageMap} - search={<AlgoliaSearch />} - sidebar={{ - defaultMenuCollapseLevel: 2, - autoCollapse: true, - }} - themeSwitch={{ - dark: dictionary.dark, - light: dictionary.light, - system: dictionary.system, - }} - toc={{ - backToTop: dictionary.toc.backToTop, - title: dictionary.toc.title, - }} - > + <AppRootProvider> <GoogleTagManager gtmId="GTM-TVS58H5F" /> <Script src="https://status.arcade.dev/embed/script.js" strategy="afterInteractive" /> <CustomLayout>{children}</CustomLayout> - </Layout> + </AppRootProvider> </body> </html> ); diff --git a/app/llms.mdx/[[...slug]]/route.ts b/app/llms.mdx/[[...slug]]/route.ts new file mode 100644 index 000000000..c799f193f --- /dev/null +++ b/app/llms.mdx/[[...slug]]/route.ts @@ -0,0 +1,42 @@ +import { notFound } from "next/navigation"; +import type { NextRequest } from "next/server"; +import { source } from "@/lib/source"; + +export const revalidate = false; + +/** `<GlossaryTerm ...>text</GlossaryTerm>` injected by lib/remark-glossary.ts. */ +const GLOSSARY_TERM_REGEX = /<GlossaryTerm\b[^>]*>([\s\S]*?)<\/GlossaryTerm>/g; + +/** + * Serves a docs page as processed Markdown (`/llms.mdx/<slug>`), used by the + * "Copy page" / "Open in ..." actions and by agents. Toolkit pages are served + * by `/api/toolkit-data/<id>` with `Accept: text/markdown` instead. + */ +export async function GET( + _req: NextRequest, + { params }: { params: Promise<{ slug?: string[] }> } +) { + const { slug } = await params; + const page = source.getPage(slug); + if (!page) { + notFound(); + } + + const markdown = (await page.data.getText("processed")).replace( + GLOSSARY_TERM_REGEX, + "$1" + ); + const header = `# ${page.data.title}\n\n${ + page.data.description ? `${page.data.description}\n\n` : "" + }`; + + return new Response(header + markdown, { + headers: { + "Content-Type": "text/markdown; charset=utf-8", + }, + }); +} + +export function generateStaticParams() { + return source.generateParams(); +} diff --git a/app/not-found.tsx b/app/not-found.tsx index 084722599..4216c60f8 100644 --- a/app/not-found.tsx +++ b/app/not-found.tsx @@ -1,182 +1,34 @@ -"use client"; - -import { - Button, - buttonVariants, - Card, - CardContent, - CardFooter, - CardHeader, -} from "@arcadeai/design-system"; -import { cn } from "@arcadeai/design-system/lib/utils"; -import { Home, SearchX } from "lucide-react"; -import Link from "next/link"; -import { usePathname, useSearchParams } from "next/navigation"; -import posthog from "posthog-js"; -import { Suspense, useEffect, useMemo, useRef, useState } from "react"; -import type { Dictionary } from "@/_dictionaries/i18n-config"; -import { BackButton } from "@/app/_components/back-button"; - -const LOCALE_PATH_REGEX = /^\/([a-z]{2}(?:-[A-Z]{2})?)(?:\/.+|$)/; -function getReferrerInfo(): { referrer: string; referringDomain: string } { - const referrer = - typeof document !== "undefined" ? document.referrer || "" : ""; - if (!referrer) { - return { referrer: "", referringDomain: "" }; - } - try { - return { referrer, referringDomain: new URL(referrer).hostname }; - } catch { - return { referrer, referringDomain: "" }; - } -} - -// Default dictionary for fallback during SSR -const defaultDict: Dictionary["notFoundPage"] = { - title: "Page not found", - description: "This page doesn't exist or may have moved.", - notAvailablePrefix: "Not available in", - tryEnglish: "Try English version", - translationHint: "This page may not be translated yet.", - viewOriginalEnglish: "View the original in English", - goHome: "Go to homepage", - goBack: "Go back", - needHelp: "Need help? Try these popular pages:", - quickstart: "Quickstart", - mcpServers: "MCP Servers", - createMcpServer: "Create a MCP Server", -}; - -function NotFoundContent() { - const pathname = usePathname() || "/"; - const searchParams = useSearchParams(); - const lastCapturedPathRef = useRef<string | null>(null); - const [dict, setDict] = useState<Dictionary["notFoundPage"]>(defaultDict); - - const currentLocale = useMemo( - () => pathname.match(LOCALE_PATH_REGEX)?.[1] || "en", - [pathname] - ); - - useEffect(() => { - const loadDict = async () => { - try { - const dictModule = await import("@/_dictionaries/en"); - setDict(dictModule.default.notFoundPage); - } catch { - // Keep default English dictionary on error - } - }; - loadDict(); - }, []); - - useEffect(() => { - const search = searchParams?.toString(); - const pathWithQuery = search ? `${pathname}?${search}` : pathname; - - // Prevent duplicate capture if this component re-renders for the same 404 URL. - if (lastCapturedPathRef.current === pathWithQuery) { - return; - } - lastCapturedPathRef.current = pathWithQuery; - - const { referrer, referringDomain } = getReferrerInfo(); - - posthog.capture("Page not found", { - status_code: 404, - pathname, - path: pathWithQuery, - locale: currentLocale, - // Ensure special props are attached to custom 404 events for link analysis. - $current_url: typeof window !== "undefined" ? window.location.href : null, - $referrer: referrer || null, - $referring_domain: referringDomain || null, - }); - }, [currentLocale, pathname, searchParams]); - - return ( - <div className="flex items-center justify-center py-16"> - <div className="w-full max-w-xl"> - <Card className="gap-4 border-2 border-stone-900 py-6 shadow-2xl shadow-black/10 dark:shadow-black/25"> - <CardHeader className="text-center"> - {/* Illustration */} - <div className="mx-auto flex size-16 items-center justify-center rounded-full bg-linear-to-br from-orange-100 to-orange-50 ring-1 ring-orange-200/50 dark:from-orange-900/30 dark:to-orange-800/20 dark:ring-orange-800/30"> - <SearchX className="size-9 text-orange-600 dark:text-orange-400" /> - </div> - </CardHeader> - - <CardContent className="space-y-6 px-10"> - {/* Content */} - <div className="space-y-3 text-center"> - <h1 className="font-bold text-2xl text-foreground tracking-tight sm:text-3xl"> - {dict.title} - </h1> - <p className="mx-auto max-w-sm text-base text-muted-foreground leading-relaxed"> - {dict.description} - </p> - </div> - - {/* Actions */} - <div className="flex flex-col items-center justify-center gap-3 sm:flex-row"> - <Button - render={<Link href={`/${currentLocale}/home`} />} - size="lg" - > - <Home className="mr-2 h-5 w-5" /> - {dict.goHome} - </Button> - <BackButton goBackText={dict.goBack} /> - </div> - </CardContent> - - <CardFooter className="border-border border-t pt-6"> - <div className="w-full space-y-3 text-center"> - <p className="text-muted-foreground text-sm">{dict.needHelp}</p> - <div className="flex flex-wrap items-center justify-center gap-2"> - <Link - className={cn( - buttonVariants({ variant: "ghost", size: "sm" }), - "h-8" - )} - href={`/${currentLocale}/home/quickstart`} - > - {dict.quickstart} - </Link> - <Link - className={cn( - buttonVariants({ variant: "ghost", size: "sm" }), - "h-8" - )} - href={`/${currentLocale}/mcp-servers`} - > - {dict.mcpServers} - </Link> - <Link - className={cn( - buttonVariants({ variant: "ghost", size: "sm" }), - "h-8" - )} - href={`/${currentLocale}/home/build-tools/create-a-mcp-server`} - > - {dict.createMcpServer} - </Link> - </div> - </div> - </CardFooter> - </Card> - </div> - </div> - ); -} - +import { Suspense } from "react"; +import { ChromeLayout } from "@/components/chrome/docs-layout"; +import { NotFoundContent } from "@/components/chrome/not-found-content"; +import { buildSidebarModel } from "@/components/chrome/sidebar-model"; +import { SiteFooter } from "@/components/chrome/site-footer"; +import { source } from "@/lib/source"; + +/** + * 404 page: the navbar and footer of the docs, without the sidebar. The + * mobile menu still lists the docs so a reader can find their way back. + */ export default function NotFound() { + const tree = source.getPageTree(); return ( - <Suspense - fallback={ - <div className="flex items-center justify-center py-16">Loading...</div> - } - > - <NotFoundContent /> - </Suspense> + <> + <ChromeLayout + bare + directories={buildSidebarModel(tree, "/en")} + tree={tree} + > + <Suspense + fallback={ + <div className="flex items-center justify-center py-16"> + Loading... + </div> + } + > + <NotFoundContent /> + </Suspense> + </ChromeLayout> + <SiteFooter /> + </> ); } diff --git a/app/sitemap.ts b/app/sitemap.ts index 87f26c12b..483c387ed 100644 --- a/app/sitemap.ts +++ b/app/sitemap.ts @@ -1,58 +1,58 @@ import fs from "node:fs/promises"; import path from "node:path"; import type { MetadataRoute } from "next"; +import { source } from "@/lib/source"; +import { listValidIntegrationLinks } from "@/lib/toolkit-static-params"; import { resolveToolkitDataDir } from "@/toolkit-docs-generator/src/shared/toolkit-data-dir"; -import { listValidIntegrationLinks } from "./_lib/toolkit-static-params"; const SITE_URL = process.env.SITE_URL ?? "https://docs.arcade.dev"; const NORMALIZED_SITE_URL = SITE_URL.replace(/\/+$/, ""); -const APP_DIR = path.join(process.cwd(), "app"); +const CONTENT_DIR = path.join(process.cwd(), "content", "docs"); const TOOLKIT_DATA_DIR = resolveToolkitDataDir(); -const SKIP_DIRS = new Set(["_meta", "_api", "_redirects", "api"]); -const INDEX_SUFFIX_REGEX = /\/index$/; let cachedRoutes: Promise<MetadataRoute.Sitemap> | null = null; -async function collectRoutes(dir: string): Promise<MetadataRoute.Sitemap> { - const dirs = await fs.readdir(dir, { withFileTypes: true }); - const entries: MetadataRoute.Sitemap = []; +/** + * Page slug segments left out of the sitemap. The published sitemap has never + * listed /en/references/api, and this keeps the URL set identical; remove + * "api" from this set to start listing the page. + */ +const SKIPPED_SEGMENTS = new Set(["api"]); - for (const entry of dirs) { - if (entry.isDirectory()) { - if (SKIP_DIRS.has(entry.name) || entry.name.includes("[")) { - continue; +/** + * One entry per MDX page known to the Fumadocs source. `lastModified` comes + * from the file's mtime; when the file cannot be stat-ed (for example a virtual page) the entry is emitted + * without a date rather than dropped. + */ +async function collectRoutes(): Promise<MetadataRoute.Sitemap> { + const pages = source + .getPages() + .filter( + (page) => !page.slugs.some((segment) => SKIPPED_SEGMENTS.has(segment)) + ); + + return await Promise.all( + pages.map(async (page) => { + const entry: MetadataRoute.Sitemap[number] = { + url: `${NORMALIZED_SITE_URL}${page.url}`, + }; + try { + const stats = await fs.stat(path.join(CONTENT_DIR, page.path)); + entry.lastModified = stats.mtime; + } catch { + // No file on disk for this page; leave lastModified unset. } - const childRoutes = await collectRoutes(path.join(dir, entry.name)); - entries.push(...childRoutes); - continue; - } - - if (entry.isFile() && entry.name === "page.mdx") { - const filePath = path.join(dir, entry.name); - const stats = await fs.stat(filePath); - - const relativeDir = path - .relative(APP_DIR, dir) - .replace(/\\/g, "/") - .replace(INDEX_SUFFIX_REGEX, ""); - - const routePath = relativeDir ? `/${relativeDir}` : "/"; - entries.push({ - url: `${NORMALIZED_SITE_URL}${routePath}`, - lastModified: stats.mtime, - }); - } - } - - return entries; + return entry; + }) + ); } /** - * `[toolkitId]` directories are skipped above because they aren't literal - * URLs — but `listValidIntegrationLinks()` (the same enumeration the - * integrations index page uses) already resolves every toolkit that dynamic - * route serves, plus a handful of authored static partner pages living - * alongside it. Skip any link the directory walk already found (the static - * ones) so it isn't listed twice. + * Toolkit pages are rendered from JSON, not MDX, so the Fumadocs source never + * sees them — but `listValidIntegrationLinks()` (the same enumeration the + * integrations index page uses) resolves every toolkit the catch-all route + * serves, plus a handful of authored static partner pages living alongside + * them. Skip any link the MDX walk already found (the static ones) so it isn't + * listed twice. */ async function collectToolkitRoutes( existingPaths: Set<string> @@ -108,7 +108,7 @@ async function getToolkitDataMtime(): Promise<Date> { export default function sitemap(): Promise<MetadataRoute.Sitemap> { if (!cachedRoutes) { cachedRoutes = (async () => { - const routes = await collectRoutes(APP_DIR); + const routes = await collectRoutes(); const existingPaths = new Set( routes.map((route) => route.url.slice(NORMALIZED_SITE_URL.length)) ); diff --git a/biome.jsonc b/biome.jsonc index 71566f6e3..66fb0395e 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -52,7 +52,7 @@ } }, { - "includes": ["app/_components/toolkit-docs/**"], + "includes": ["components/toolkit-docs/**"], "linter": { "rules": { "complexity": { @@ -131,7 +131,8 @@ "!extract-custom-sections", "!custom-sections-transformer", "!examples", - "!app/globals.css" + "!app/global.css", + "!.source" ] } } diff --git a/bun.lock b/bun.lock new file mode 100644 index 000000000..709796808 --- /dev/null +++ b/bun.lock @@ -0,0 +1,2139 @@ +{ + "lockfileVersion": 2, + "configVersion": 1, + "workspaces": { + "": { + "name": "arcade-docs", + "dependencies": { + "@arcadeai/design-system": "7.11.0", + "@arcadeai/ui-kit": "0.14.0", + "@mdx-js/mdx": "3.1.1", + "@next/third-parties": "16.3.8", + "@ory/client": "1.22.37", + "@shikijs/transformers": "4.5.0", + "@uidotdev/usehooks": "2.4.1", + "algoliasearch": "5.53.0", + "fumadocs-core": "16.15.18", + "fumadocs-mdx": "15.4.6", + "fumadocs-ui": "16.15.18", + "github-slugger": "2.0.0", + "lucide-react": "0.577.0", + "mermaid": "11.17.2", + "motion": "12.40.0", + "next": "16.3.8", + "next-themes": "0.4.6", + "posthog-js": "1.379.0", + "posthog-node": "5.35.12", + "react": "19.2.7", + "react-dom": "19.2.7", + "react-hook-form": "7.77.0", + "react-instantsearch": "7.34.0", + "react-markdown": "10.1.0", + "react-syntax-highlighter": "16.1.1", + "remark-gfm": "4.0.1", + "swagger-ui-react": "5.32.14", + "tailwindcss-animate": "1.0.7", + "unist-util-visit": "5.1.0", + "yaml": "2.8.3", + "zod": "4.3.6", + }, + "devDependencies": { + "@anthropic-ai/sdk": "0.91.1", + "@biomejs/biome": "2.3.2", + "@octokit/rest": "22.0.1", + "@tailwindcss/postcss": "4.3.0", + "@tailwindcss/typography": "0.5.19", + "@types/bun": "1.4.2", + "@types/hast": "3.0.5", + "@types/mdast": "4.0.4", + "@types/mdx": "2.0.13", + "@types/node": "22.19.17", + "@types/react": "19.2.16", + "@types/react-dom": "19.2.3", + "@types/react-syntax-highlighter": "15.5.13", + "@types/unist": "3.0.3", + "chalk": "5.6.2", + "commander": "14.0.3", + "dotenv": "17.4.2", + "fast-glob": "3.3.3", + "husky": "9.1.7", + "lint-staged": "16.4.0", + "mdast-util-to-string": "4.0.0", + "next-validate-link": "1.6.6", + "openai": "6.41.0", + "ora": "9.4.0", + "postcss": "8.5.26", + "tailwindcss": "4.3.0", + "tsx": "^4.23.8", + "typescript": "5.9.3", + "ultracite": "6.1.0", + "vite": "7.3.5", + "vitest": "4.1.8", + }, + }, + }, + "overrides": { + "@opentelemetry/core": ">=2.8.0 <2.11.0", + "@xmldom/xmldom": ">=0.9.12", + "axios": ">=1.19.0 <2", + "brace-expansion": ">=5.0.12 <6", + "dompurify": ">=3.4.13 <4", + "esbuild": "0.28.1", + "fflate": ">=0.4.9 <0.5", + "form-data": "4.0.6", + "js-yaml": ">=4.3.1 <5", + "nanoid": ">=3.3.18 <4", + "postcss": "8.5.26", + "protobufjs": ">=7.6.3 <8", + "qs": ">=6.16.0 <7", + "seroval": "1.6.8", + "seroval-plugins": "1.6.8", + "sharp": "0.35.5", + "source-map-js": "1.2.2", + }, + "packages": { + "@ai-sdk/gateway": ["@ai-sdk/gateway@4.0.103", "", { "dependencies": { "@ai-sdk/provider": "4.0.21", "@ai-sdk/provider-utils": "5.0.53", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-nnGTUHzPRQ14jhv9CL4OQrzcRBIFTBMIPKGIi9Z9u/4GtwSRxa/nxUX6np2Iwp5phOzO0a0njj/j9Jolf5AEaA=="], + + "@ai-sdk/mcp": ["@ai-sdk/mcp@2.0.66", "", { "dependencies": { "@ai-sdk/provider": "4.0.21", "@ai-sdk/provider-utils": "5.0.53", "cross-spawn": "^7.0.6", "pkce-challenge": "^5.0.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-wFAL5rL/CQljgrhrtpzvrmLJ1tY7Q3o9+SQR+02ZYff3jzXOpj3vfCdi6PjJ1jS3jDd8rkmE9QFlfOFKztI3pg=="], + + "@ai-sdk/provider": ["@ai-sdk/provider@4.0.21", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-UpbC9C1oht8dhfKPbVXSLRZS3DI8uk8n5v2uMjBPNIYGsC2kL045ywq7D8Hh9KgnH9rP5W/GxQdYtWScRouqlA=="], + + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@5.0.53", "", { "dependencies": { "@ai-sdk/provider": "4.0.21", "@standard-schema/spec": "^1.1.0", "@workflow/serde": "4.1.0", "eventsource-parser": "^3.0.8", "undici": "^7.29.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-VVe6UDd0y0/B4TfauuKKzcppqvhTmSluwwUVT4WG2/8h7gFbyFm7PM4UGM+ZXe3/RI6bv+Vl1cw/7aiBoQqdxg=="], + + "@ai-sdk/react": ["@ai-sdk/react@4.0.130", "", { "dependencies": { "@ai-sdk/mcp": "2.0.66", "@ai-sdk/provider": "4.0.21", "@ai-sdk/provider-utils": "5.0.53", "ai": "7.0.127", "swr": "^2.4.1", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" } }, "sha512-6nUGT2mWIPUK/s2nUJH0Hiu90Oqawwo2INAHekOi4hJBb8zhFJS0yMP0aRn/+UO9VmJT/v58d8rWSmGE3B6ApA=="], + + "@algolia/abtesting": ["@algolia/abtesting@1.19.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-Lhnez3hhXHk25lfxLAMxvkP4fmN3+1RgADhD2ssMDBYuAsDVReeyP+3SGRx+ntq8ijMrLqUyfvO72TB6jsTteQ=="], + + "@algolia/client-abtesting": ["@algolia/client-abtesting@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-0ZjA5Hcmaoz5Lj6OG0zhfIyeqzJZnLW2CRJA1W17UwMFGRtZAJ9yJKRvPEDA6gkpsIoQxORTSW6sWFiuYncPNQ=="], + + "@algolia/client-analytics": ["@algolia/client-analytics@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-kWNodP75iiEaOtemC9F/hlxNBG5E2QUjN1BusnE6m2b4l7Qh/BUO3fGCVsmKJI65VO4VKGGmT43ICvHtTcJ2JQ=="], + + "@algolia/client-common": ["@algolia/client-common@5.53.0", "", {}, "sha512-YPN45TXD9Wrse185t/Ta7nktZsqpv97oOjCzp2sblHnCL6rBc9TDeJAg1IGl2UpdwnSD05Zu/5wLB4watOUMyg=="], + + "@algolia/client-insights": ["@algolia/client-insights@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-qAcYTDJE6m924FDDUQvdD6vh7DYaqOeSpFS74IP37/JRV0v4cGBauyxTF2WzDnokUylQDbqreoFIJZfg0Fitmw=="], + + "@algolia/client-personalization": ["@algolia/client-personalization@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-fQaY+DkSJOpuUVUe8MQTwrdiKAqkJGhpDarB08duBn/sUv7Bkib6MDRQauCcWTWTe4HIW+EbwQP9R4kci1V/Yw=="], + + "@algolia/client-query-suggestions": ["@algolia/client-query-suggestions@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-o72tsiEZGfeS/dxL9IADfzcZWGEwKDEe5CvtrBuT//3JR+SHuTtHRI2ZTf7D7bcKagcbojvO8hnkHdfoakSlYg=="], + + "@algolia/client-search": ["@algolia/client-search@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-Ds16IyPm/dNJPCU8OzApo2gwGrgWT5BYHhE3NFwZbpCveqyvPDB9sZDDkJ5DsdOGT2aC+R3i0/M1OVXF2qdgPg=="], + + "@algolia/events": ["@algolia/events@4.0.1", "", {}, "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ=="], + + "@algolia/ingestion": ["@algolia/ingestion@1.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-oNbT6z4NwD8Pou9VPINGlN/tlG1afESh2EbxqnP6rwl95xKVD/Zlciis1PpNeO/9U/rrajc1+7DcfKi03tX1KQ=="], + + "@algolia/monitoring": ["@algolia/monitoring@1.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-G+KZb/yd+qAOFn/cEvTGeLxQm8aP3a0od50l3z/ylccY+/o4YG3TNcjU1tFQHW4mXC137GPyR7W70R0kRQDLnA=="], + + "@algolia/recommend": ["@algolia/recommend@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-6aVfYd55Un6IUgPLbo84WfgFZlS3L0vA1ttzXL5vahHewUJ8jYgd89TzlWRTeej7w70mb9RWsVlFYGmJ/diQww=="], + + "@algolia/requester-browser-xhr": ["@algolia/requester-browser-xhr@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0" } }, "sha512-ke27DqgzCOlt+RbeEdCxtXxMQOnAOi8ujr2wid0DmDKzR95Kw/f9sBsuhBxtjevCqJRJszfRTLY0B1pbO6IhkA=="], + + "@algolia/requester-fetch": ["@algolia/requester-fetch@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0" } }, "sha512-GngiOqt2Gq4oLno6yXQVj9om+qSO9SWAoduoTOEg79dKZ62brB8OOIvSJG/vDNoanYi6a7Al9uDZwXvi+bcVTg=="], + + "@algolia/requester-node-http": ["@algolia/requester-node-http@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0" } }, "sha512-6mF9LZMUk0QqWvrnxkxBqhswwz6Xfiwy6/gmTzL5HrlhdVG3ITAqGV2k3XmVThP1h0Ulc3VQwiNCD7/Nr4JNlQ=="], + + "@alloc/quick-lru": ["@alloc/quick-lru@5.3.0", "", {}, "sha512-U4+70Pc5ZS9osnCBCE5Jha/ciHM+Yp+CNMNC/7HvYbNRk1Ldd+f7qO65W5qfhu/TCv+/ozljlXXe9Nj8419DMA=="], + + "@antfu/install-pkg": ["@antfu/install-pkg@2.1.0", "", { "dependencies": { "package-manager-detector": "^1.8.0", "tinyexec": "^1.3.1" } }, "sha512-sdg9NxU3zR4Mnawfbc/x6GB5Wf17WYud5qOuEuxXjaKpYpMkISSJEjItGebXJ2bQ4DIcly4NYH23mtkGJjvKUw=="], + + "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.91.1", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw=="], + + "@arcadeai/arcadejs": ["@arcadeai/arcadejs@2.4.1", "", { "bin": { "arcadeai-arcadejs": "bin/cli" } }, "sha512-DLpvvDBvRNjzVmFYJ+upPrUJ8YRBoGWqPfzhuFp2+lh5Shxm0l9qLwe1Ah4pKvuCatU7TLOOTkFpICitTuuJeQ=="], + + "@arcadeai/design-system": ["@arcadeai/design-system@7.11.0", "", { "dependencies": { "@base-ui/react": "1.6.0", "@base-ui/utils": "0.3.1", "@fontsource-variable/geist": "5.2.9", "@streamdown/code": "1.1.1", "class-variance-authority": "0.7.1", "clsx": "2.1.1", "cmdk": "1.1.1", "date-fns": "4.4.0", "react-day-picker": "10.0.1", "react-resizable-panels": "4.11.2", "react-shiki": "0.10.1", "remark-breaks": "4.0.0", "remark-gfm": "4.0.1", "streamdown": "2.5.0", "tailwind-merge": "3.6.0", "use-stick-to-bottom": "1.1.6" }, "peerDependencies": { "@hookform/resolvers": ">=5.2.1", "lucide-react": ">=0.544.0", "react": ">=19.2.1", "react-dom": ">=19.2.1", "react-hook-form": ">=7.62.0", "recharts": ">=3.2.0", "tailwindcss": ">=4.1.13" } }, "sha512-zsrmQf6yryIrqPJXljLxazFSvElj8fdSBZAC24WevD7hMcS9h0tkurAqyQevbHP0iisWCgNPYhlrj6GZIC+G4g=="], + + "@arcadeai/ui-kit": ["@arcadeai/ui-kit@0.14.0", "", { "dependencies": { "@arcadeai/arcadejs": "2.4.1", "@tanstack/react-virtual": "3.14.3", "usehooks-ts": "3.1.1" }, "peerDependencies": { "@ai-sdk/react": ">=3.0.0", "@arcadeai/design-system": ">=7.0.0", "@tanstack/react-query": ">=5.0.0", "@tanstack/react-router": ">=1.0.0", "ai": ">=6.0.0", "lucide-react": ">=0.400.0", "react": ">=19.0.0", "react-dom": ">=19.0.0", "tailwindcss": ">=4.0.0" } }, "sha512-/VhqP2+mQ8fbjvGiq6pcYISODuKbcXHfhsD4rNO79P/bFEY5A9xupG1BreDWwpwOQm6sNy8fjwLs7sbQz7KL2Q=="], + + "@babel/runtime": ["@babel/runtime@7.29.7", "", {}, "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw=="], + + "@babel/runtime-corejs3": ["@babel/runtime-corejs3@7.29.7", "", { "dependencies": { "core-js-pure": "^3.48.0" } }, "sha512-ppj9ouYku+RX0ljtgZd+KMO5mkM2bCqg8H2PYAFWnLsHEIKIdRojqbJ2i3eVHrisuxy7nOFCmngTDdWtUCdXUQ=="], + + "@base-ui/react": ["@base-ui/react@1.6.0", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@base-ui/utils": "0.3.1", "@floating-ui/react-dom": "^2.1.8", "@floating-ui/utils": "^0.2.11", "use-sync-external-store": "^1.6.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" }, "optionalPeers": ["@date-fns/tz", "@types/react", "date-fns"] }, "sha512-/jzjTWJYXhRFO45Bev9lc3cHbmjzCMpUqbMZ2AgKy/z25mY9B6shGSNcXcjQar9n5doM0KYW1W8fcFv2jZBuMw=="], + + "@base-ui/utils": ["@base-ui/utils@0.3.1", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@floating-ui/utils": "^0.2.11", "reselect": "^5.2.0", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-gFFiltORVmW/N6IILTGxizP3PBpVpysqML1ALY5Vk0mH+7faVkCknOU31goYHN5Aoek2dkjxva1XOD2Ce9WuIg=="], + + "@biomejs/biome": ["@biomejs/biome@2.3.2", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.2", "@biomejs/cli-darwin-x64": "2.3.2", "@biomejs/cli-linux-arm64": "2.3.2", "@biomejs/cli-linux-arm64-musl": "2.3.2", "@biomejs/cli-linux-x64": "2.3.2", "@biomejs/cli-linux-x64-musl": "2.3.2", "@biomejs/cli-win32-arm64": "2.3.2", "@biomejs/cli-win32-x64": "2.3.2" }, "bin": { "biome": "bin/biome" } }, "sha512-8e9tzamuDycx7fdrcJ/F/GDZ8SYukc5ud6tDicjjFqURKYFSWMl0H0iXNXZEGmcmNUmABgGuHThPykcM41INgg=="], + + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4LECm4kc3If0JISai4c3KWQzukoUdpxy4fRzlrPcrdMSRFksR9ZoXK7JBcPuLBmd2SoT4/d7CQS33VnZpgBjew=="], + + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-jNMnfwHT4N3wi+ypRfMTjLGnDmKYGzxVr1EYAPBcauRcDnICFXN81wD6wxJcSUrLynoyyYCdfW6vJHS/IAoTDA=="], + + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-amnqvk+gWybbQleRRq8TMe0rIv7GHss8mFJEaGuEZYWg1Tw14YKOkeo8h6pf1c+d3qR+JU4iT9KXnBKGON4klw=="], + + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-2Zz4usDG1GTTPQnliIeNx6eVGGP2ry5vE/v39nT73a3cKN6t5H5XxjcEoZZh62uVZvED7hXXikclvI64vZkYqw=="], + + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-8BG/vRAhFz1pmuyd24FQPhNeueLqPtwvZk6yblABY2gzL2H8fLQAF/Z2OPIc+BPIVPld+8cSiKY/KFh6k81xfA=="], + + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-gzB19MpRdTuOuLtPpFBGrV3Lq424gHyq2lFj8wfX9tvLMLdmA/R9C7k/mqBp/spcbWuHeIEKgEs3RviOPcWGBA=="], + + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCruqQlfWjhMlOdyf5pDHOxoNm4WoyY2vZ4YN33/nuZBRstVDuqPPjS0yBkbUlLEte11FbpW+wWSlfnZfSIZvg=="], + + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.2", "", { "os": "win32", "cpu": "x64" }, "sha512-6Ee9P26DTb4D8sN9nXxgbi9Dw5vSOfH98M7UlmkjKB2vtUbrRqCbZiNfryGiwnPIpd6YUoTl7rLVD2/x1CyEHQ=="], + + "@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.2", "", {}, "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA=="], + + "@chevrotain/types": ["@chevrotain/types@11.1.2", "", {}, "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw=="], + + "@clack/core": ["@clack/core@0.5.0", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow=="], + + "@clack/prompts": ["@clack/prompts@0.11.0", "", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="], + + "@date-fns/tz": ["@date-fns/tz@1.5.0", "", {}, "sha512-lwYN/vDPeNRULcepoE/LO2Pgx+7/RV+S9ARfbc9lr2DtGkOD7pAiruHvbR1RX3Qyf6ja47EWJDMsNK5vK08DJg=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.1", "", { "os": "android", "cpu": "arm64" }, "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.28.1", "", { "os": "android", "cpu": "x64" }, "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.1", "", { "os": "linux", "cpu": "arm" }, "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.1", "", { "os": "linux", "cpu": "x64" }, "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.1", "", { "os": "none", "cpu": "x64" }, "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="], + + "@floating-ui/core": ["@floating-ui/core@1.8.0", "", { "dependencies": { "@floating-ui/utils": "^0.2.12" } }, "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.8.0", "", { "dependencies": { "@floating-ui/core": "^1.8.0", "@floating-ui/utils": "^0.2.12" } }, "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg=="], + + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.9", "", { "dependencies": { "@floating-ui/dom": "^1.8.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.12", "", {}, "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww=="], + + "@fontsource-variable/geist": ["@fontsource-variable/geist@5.2.9", "", {}, "sha512-TP+QSBG3wxKGPE33CbMy/L0Nu3qvJ6Fy81Yc4LnQ95xH+i+cfEp8fyU8/kfV14YwszxIFPhnoMTbjL71waVpyQ=="], + + "@fuma-translate/react": ["@fuma-translate/react@1.0.2", "", { "peerDependencies": { "@types/react": "*", "react": "^19.2.0", "react-dom": "^19.2.0" }, "optionalPeers": ["@types/react"] }, "sha512-uOiOtBx3nRXR8Nu1GzBf1tApgF1FErDBTHxRIAQeyQdyOoZbrNRN6H4kDCWObY4qyGeGbHydG0DHzgeUgFDMIw=="], + + "@fumadocs/tailwind": ["@fumadocs/tailwind@0.1.2", "", { "peerDependencies": { "tailwindcss": "^4.0.0" }, "optionalPeers": ["tailwindcss"] }, "sha512-VppeXgIRXjZMgpf2qijj1hcfxyeYl1s0ok8k6N3jyjiX64M7sDwnt2zGPd6dbfDybgbBvDw6C6J1e42gB5NzsA=="], + + "@fumari/image-size": ["@fumari/image-size@0.1.1", "", {}, "sha512-iaiV2wyzJ4mW358NX4CX3yGit+WjKYzpaQ+rldK9BjL/5jFpeV6ruWnZVq7MEyMn3Gd0DLi8g/79NZAAQaGscw=="], + + "@hookform/resolvers": ["@hookform/resolvers@5.9.1", "", { "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "@sinclair/typebox": ">=0.25.24", "@standard-schema/spec": "^1.0.0", "@typeschema/main": ">=0.13.7", "@vinejs/vine": "^2.0.0 || ^3.0.0 || ^4.0.0", "ajv": "^8.12.0", "ajv-errors": "^3.0.0", "ajv-formats": "^2.1.1", "arktype": "^2.0.0", "ata-validator": "^1.2.0", "class-transformer": ">=0.4.0", "class-validator": ">=0.12.0", "computed-types": "^1.0.0", "effect": "^3.10.3", "fluentvalidation-ts": "^3.0.0", "fp-ts": "^2.7.0", "io-ts": "^2.0.0", "joi": "^17.0.0 || ^18.0.0", "nope-validator": ">=0.12.0", "react-hook-form": "^7.55.0", "superstruct": ">=0.12.0", "typanion": "^3.3.2", "valibot": ">=0.31.0 || ^1.0.0-beta.4 || ^1.0.0-rc", "vest": ">=6.0.0", "yup": "^1.0.0", "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["@sinclair/typebox", "@standard-schema/spec", "@typeschema/main", "@vinejs/vine", "ajv", "ajv-errors", "ajv-formats", "arktype", "ata-validator", "class-transformer", "class-validator", "computed-types", "effect", "fluentvalidation-ts", "fp-ts", "io-ts", "joi", "nope-validator", "superstruct", "typanion", "valibot", "vest", "yup", "zod"] }, "sha512-7b7vsbraJxKgjVSA1Nur9tLwj539WGJUBLA7QNvXnFoT2pM5Z7G+6rlukk4B2/QrTZy6huRtH6wKeESPKuIr6w=="], + + "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="], + + "@iconify/utils": ["@iconify/utils@3.1.7", "", { "dependencies": { "@antfu/install-pkg": "^2.0.1", "@iconify/types": "^2.0.0", "import-meta-resolve": "^4.2.0" } }, "sha512-JZHlwdID+dy+lTgbYC8NEC4zeugqeYsc6jewvzb4c58kHauJn+X7rNwQjxz5p2qSjqaEeQoLkCIQ9v/H4PK0/w=="], + + "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], + + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg=="], + + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.4" }, "os": "darwin", "cpu": "x64" }, "sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw=="], + + "@img/sharp-freebsd-wasm32": ["@img/sharp-freebsd-wasm32@0.35.5", "", { "dependencies": { "@img/sharp-wasm32": "0.35.5" }, "os": "freebsd" }, "sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw=="], + + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw=="], + + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ=="], + + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.4", "", { "os": "linux", "cpu": "arm" }, "sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw=="], + + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A=="], + + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.3.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ=="], + + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.3.4", "", { "os": "linux", "cpu": "none" }, "sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q=="], + + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ=="], + + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw=="], + + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA=="], + + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg=="], + + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.4" }, "os": "linux", "cpu": "arm" }, "sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ=="], + + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.4" }, "os": "linux", "cpu": "arm64" }, "sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ=="], + + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.3.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA=="], + + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.3.4" }, "os": "linux", "cpu": "none" }, "sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ=="], + + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.4" }, "os": "linux", "cpu": "s390x" }, "sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA=="], + + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.4" }, "os": "linux", "cpu": "x64" }, "sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog=="], + + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.4" }, "os": "linux", "cpu": "arm64" }, "sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg=="], + + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.4" }, "os": "linux", "cpu": "x64" }, "sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A=="], + + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.5", "", { "dependencies": { "@emnapi/runtime": "^1.11.3" } }, "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ=="], + + "@img/sharp-webcontainers-wasm32": ["@img/sharp-webcontainers-wasm32@0.35.5", "", { "dependencies": { "@img/sharp-wasm32": "0.35.5" }, "cpu": "none" }, "sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA=="], + + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.35.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.5", "", { "os": "win32", "cpu": "x64" }, "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.6.0", "", {}, "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "acorn": "^8.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="], + + "@mermaid-js/parser": ["@mermaid-js/parser@1.2.1", "", { "dependencies": { "@chevrotain/types": "~11.1.2" } }, "sha512-n12NohV3mrUyUL2o93IgG/ifeW9FTyeJn3zDxkhwa8MJ9Fxg3HQMlA3RiGmD/3UnJvheztkjjQAjA2T4LmUcpw=="], + + "@napi-rs/lzma-linux-x64-gnu": ["@napi-rs/lzma-linux-x64-gnu@1.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ=="], + + "@next/env": ["@next/env@16.3.8", "", {}, "sha512-Al9zqHVV7TJv0eFuOU4U7Lvv74PTih4Ch63sk2xCIpSTkE3udFnaOcnzP2lQVymiL7yS9Cj2iClUXlR3EQ5sEw=="], + + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.3.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2JPRMh2nmQG5CiL7cXGL9AGwnPWJQ//cTtAUCT+w511QHk79SYz3LGv/pc5X643B/WEO0rvu3Yww0hqwt3kgeA=="], + + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.3.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-GZtCCOBKJ4leVIT/Th0llWKhD1ca92lzbQiS5R5ON9QkoiFnilFsebDae1JU2a3HWoKMEmEZWGs1AGLavVM72Q=="], + + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.3.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-O659ygeQYqneJ1fBKMpFxIFqYkYswu8IAS1OCKK/4f3ZgJJm1dRz4fVJZRi/kLLWjnBKnebOePA4WNv+sV1pVA=="], + + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.3.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-dSjKSyWpzxoO1d3DIZZcP4XJcNaKeLmxQMFOiYl5vuBRMmweIqnAhty8tAmRsvTss779cK1FtYnDMj40e4TQlg=="], + + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.3.8", "", { "os": "linux", "cpu": "x64" }, "sha512-lbqOuz3RPRcv+o9msNsJw5x4+Y1ZwPTs6vmL6DCf7i0fZfvng/F59wyeDwqHIvV0mK//RBy/jJkZ+nCKsSMXjQ=="], + + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.3.8", "", { "os": "linux", "cpu": "x64" }, "sha512-+316WswI8ScVgZeUd+1KGaXkHhaYQzCjvH/05TZSpJ8zBizb1a4G7DtO7F12jcBIqMOtsz9ji1t48fmKtzqsGA=="], + + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.3.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-ji0gd4kMYUxO+1fJBIbiBVRCjzG/lloiyCccnlebvb1ZJ5qXCPZqYg4Jl1DrrixnWNMKylzgpmMWx0yNDYXlzw=="], + + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.3.8", "", { "os": "win32", "cpu": "x64" }, "sha512-WcTlaKt/TWkh5kUjdJcUmB1XgZ+1c6fz4Y9fDHL73YNSdGaUWjceeWrrlwF0nv19iABYWC4iAq1oX1w4Bn0vfg=="], + + "@next/third-parties": ["@next/third-parties@16.3.8", "", { "dependencies": { "third-party-capital": "1.0.20" }, "peerDependencies": { "next": "^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0-beta.0", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0" } }, "sha512-6Ee+OFZczvnG0FVsHaEjQeMRy1QQ+TNHSz7VxcBh87WQqrp/ahaYvSekmlBPtRuh6AD10fRmz1Pws5UcWrokxA=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@octokit/auth-token": ["@octokit/auth-token@6.0.0", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="], + + "@octokit/core": ["@octokit/core@7.0.8", "", { "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.5", "@octokit/request": "^10.0.16", "@octokit/request-error": "^7.1.2", "@octokit/types": "^18.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-L7y8eYc+AwxGr2PWI4WFt1VG4TiJ66c26BD16mXpYIlXxG0SMigM1+m4aTSlYyBr5BlQsGAlz8uDCoZN4SEMcg=="], + + "@octokit/endpoint": ["@octokit/endpoint@11.0.5", "", { "dependencies": { "@octokit/types": "^18.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-iXa654H3yFafF/ieHkukfbgWo2rmXD2ceD0ZOtrPhw1bc3FDch1d9N/TNs0FQ1/cIbwb7kspUX8jzIs8nzb9DQ=="], + + "@octokit/graphql": ["@octokit/graphql@9.0.5", "", { "dependencies": { "@octokit/request": "^10.0.16", "@octokit/types": "^18.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-bt/hm03LeU6Vy7FwTrkkC9p3XGT/lBwClglMqxBSe5/q0E5CdJTXeAqEI0vlw89/LF/G6tryTIH8HirZ3prMVg=="], + + "@octokit/openapi-types": ["@octokit/openapi-types@29.0.1", "", {}, "sha512-9qWOMFNxxLokERcms42rU0PTLqQmVs7g5E41TI4mCOxmpFayD1rfC7XxOL55cG9MBZLFlC31BrR37myMKardwg=="], + + "@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@14.0.0", "", { "dependencies": { "@octokit/types": "^16.0.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw=="], + + "@octokit/plugin-request-log": ["@octokit/plugin-request-log@6.0.0", "", { "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q=="], + + "@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@17.0.0", "", { "dependencies": { "@octokit/types": "^16.0.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw=="], + + "@octokit/request": ["@octokit/request@10.0.16", "", { "dependencies": { "@octokit/endpoint": "^11.0.5", "@octokit/request-error": "^7.1.2", "@octokit/types": "^18.0.0", "content-type": "^3.0.0", "json-with-bigint": "^3.5.12", "universal-user-agent": "^7.0.2" } }, "sha512-A0zWGjHzISIb+9ccG8s0dq7LKO5zVpJLRICjgUb+sJxEWqn8RUHB1rD3AE51+PECvXHIxqZ1VVvs4fHTSD9nUQ=="], + + "@octokit/request-error": ["@octokit/request-error@7.1.2", "", { "dependencies": { "@octokit/types": "^18.0.0" } }, "sha512-XZRuT3xZ84D3gYErI1DZvhJ33dCWVV6uzBtWkaBB4TvA/L6eOeTZodxLFVB44bBEEo3vEx7y00UfX1tBLrtLRg=="], + + "@octokit/rest": ["@octokit/rest@22.0.1", "", { "dependencies": { "@octokit/core": "^7.0.6", "@octokit/plugin-paginate-rest": "^14.0.0", "@octokit/plugin-request-log": "^6.0.0", "@octokit/plugin-rest-endpoint-methods": "^17.0.0" } }, "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw=="], + + "@octokit/types": ["@octokit/types@18.0.0", "", { "dependencies": { "@octokit/openapi-types": "^29.0.1" } }, "sha512-l6bAF43PNxkJp6g+W4PjoUSSkxHomXw2nOum5CTftJz1NlV3vu93NImgOYtLf6CbBUb5j+fiuzW0PPQ5JTSvZA=="], + + "@opentelemetry/api": ["@opentelemetry/api@1.9.1", "", {}, "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q=="], + + "@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.208.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg=="], + + "@opentelemetry/core": ["@opentelemetry/core@2.10.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-/wNZ8twnEQQA4HoHu22+vcsdru6pWPWxW+7w+FlxT6Id7PE/WIbZmVKkte+PF72e0F2dnImFeHD2syyE1Mw6MQ=="], + + "@opentelemetry/exporter-logs-otlp-http": ["@opentelemetry/exporter-logs-otlp-http@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-exporter-base": "0.208.0", "@opentelemetry/otlp-transformer": "0.208.0", "@opentelemetry/sdk-logs": "0.208.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-jOv40Bs9jy9bZVLo/i8FwUiuCvbjWDI+ZW13wimJm4LjnlwJxGgB+N/VWOZUTpM+ah/awXeQqKdNlpLf2EjvYg=="], + + "@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.208.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-transformer": "0.208.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA=="], + + "@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.208.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ=="], + + "@opentelemetry/resources": ["@opentelemetry/resources@2.12.0", "", { "dependencies": { "@opentelemetry/core": "2.12.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-+rSRoOdln6NhXTVcBdHSlZxJDNzwxadeweK2ltZO3tVtfp6aklABc06xclDk6ovRR+EiGgIcSbh/6NgjFhnb3A=="], + + "@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA=="], + + "@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw=="], + + "@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw=="], + + "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.43.0", "", {}, "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg=="], + + "@ory/client": ["@ory/client@1.22.37", "", { "dependencies": { "axios": "^1.6.1" } }, "sha512-UEYHSveRRD3TCT0JB54ZKQoRMEHn7HTsNsMOjz2t3bzCs5aNjgpjYIBK4z5zE2PwNrcInZyQXfRiwK+LIUaOug=="], + + "@oxc-project/types": ["@oxc-project/types@0.152.0", "", {}, "sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw=="], + + "@posthog/core": ["@posthog/core@1.30.3", "", { "dependencies": { "@posthog/types": "1.379.0" } }, "sha512-mGIN4Du1a8fKxV5WfnEtogq3VOQLa4PfWMUg5uMIMDD+fp5bhTA4QG4oYAea4vBTZ1ZOanQbjXcX4b5k2X93Qw=="], + + "@posthog/types": ["@posthog/types@1.379.0", "", {}, "sha512-QJLzyV22iZKy5eEXKn9IB1swSVo2JigIawebBha+BmsePsY3XA0HAcq2B+D8Km5BHnbE+SGm5aHxFG3DlVE1yg=="], + + "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="], + + "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="], + + "@protobufjs/codegen": ["@protobufjs/codegen@2.0.5", "", {}, "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g=="], + + "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.1", "", {}, "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg=="], + + "@protobufjs/fetch": ["@protobufjs/fetch@1.1.1", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1" } }, "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw=="], + + "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="], + + "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="], + + "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="], + + "@protobufjs/utf8": ["@protobufjs/utf8@1.1.2", "", {}, "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug=="], + + "@radix-ui/number": ["@radix-ui/number@1.1.3", "", {}, "sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA=="], + + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.7", "", {}, "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q=="], + + "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.20", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-collapsible": "1.1.20", "@radix-ui/react-collection": "1.1.15", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-direction": "1.1.4", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-jDhG9FvAEnlhnjrsINbNXcUa4G+L1KqSkJSunkbKEzFRcAb52jvM0PjPxPRvhe1HNc5F5yc0yzzWeeqlH4yBIg=="], + + "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.15", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA=="], + + "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.20", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-controllable-state": "1.2.6", "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-mcGesGplBnzN2sbvJETzpCNfSMyPnb29q1GRLU+Ib7bJrpIG2ywmRoh2V5VbA2uNvKikKUlVbAPks7JDjz4A8Q=="], + + "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.15", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-slot": "1.3.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA=="], + + "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.5", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA=="], + + "@radix-ui/react-context": ["@radix-ui/react-context@1.2.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA=="], + + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.23", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-dismissable-layer": "1.1.19", "@radix-ui/react-focus-guards": "1.1.6", "@radix-ui/react-focus-scope": "1.1.16", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-portal": "1.1.17", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-slot": "1.3.3", "@radix-ui/react-use-controllable-state": "1.2.6", "@radix-ui/react-use-layout-effect": "1.1.4", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA=="], + + "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg=="], + + "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.19", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-callback-ref": "1.1.4", "@radix-ui/react-use-effect-event": "0.0.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w=="], + + "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.6", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ=="], + + "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.16", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-callback-ref": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ=="], + + "@radix-ui/react-id": ["@radix-ui/react-id@1.1.4", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA=="], + + "@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.22", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-collection": "1.1.15", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-direction": "1.1.4", "@radix-ui/react-dismissable-layer": "1.1.19", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-callback-ref": "1.1.4", "@radix-ui/react-use-controllable-state": "1.2.6", "@radix-ui/react-use-layout-effect": "1.1.4", "@radix-ui/react-use-previous": "1.1.4", "@radix-ui/react-visually-hidden": "1.2.11" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ou7iLEJ+yrhQndkkA4U21XIdS/CS45F4iXIkTZcb6/Ne9EMsOuDudVmCwmDnfFZZ+y1FZqXRNSIgBy+YMvZVZg=="], + + "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.23", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-dismissable-layer": "1.1.19", "@radix-ui/react-focus-guards": "1.1.6", "@radix-ui/react-focus-scope": "1.1.16", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-popper": "1.3.7", "@radix-ui/react-portal": "1.1.17", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-slot": "1.3.3", "@radix-ui/react-use-controllable-state": "1.2.6", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ=="], + + "@radix-ui/react-popper": ["@radix-ui/react-popper@1.3.7", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.15", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-callback-ref": "1.1.4", "@radix-ui/react-use-layout-effect": "1.1.4", "@radix-ui/react-use-rect": "1.1.4", "@radix-ui/react-use-size": "1.1.4", "@radix-ui/rect": "1.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg=="], + + "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.17", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ=="], + + "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.10", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw=="], + + "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.10", "", { "dependencies": { "@radix-ui/react-slot": "1.3.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg=="], + + "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.19", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-collection": "1.1.15", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-direction": "1.1.4", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-callback-ref": "1.1.4", "@radix-ui/react-use-controllable-state": "1.2.6", "@radix-ui/react-use-is-hydrated": "0.1.3", "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ=="], + + "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.18", "", { "dependencies": { "@radix-ui/number": "1.1.3", "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-direction": "1.1.4", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-callback-ref": "1.1.4", "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Zn5Cd171wxsO3Dfg8HaW6RifTb9CYTKQJHs/G4+LN1GfmJpaQMZQyQxMprVPHpaz7QY4l9BxK2JwQuzHsXC8nA=="], + + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.5" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q=="], + + "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.21", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-direction": "1.1.4", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-roving-focus": "1.1.19", "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-UKxJlZid7FVtsk/WTxj4i4uSEgj2Au+KBbS7SQyTlzMhhn+86Cz3tISZdTa87bfEfcuvZezf2ZsxD4xuEKtkog=="], + + "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ=="], + + "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-use-effect-event": "0.0.5", "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ=="], + + "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.5", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg=="], + + "@radix-ui/react-use-is-hydrated": ["@radix-ui/react-use-is-hydrated@0.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw=="], + + "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw=="], + + "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-XoSLhbRbqxFtgJoi2fNHA3C6pDlY34x508vUpUGoFZfvePfHXHbE1lC4FYFMnJWgiCRroSTw6fOsXQoVS9RwZg=="], + + "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.4", "", { "dependencies": { "@radix-ui/rect": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ=="], + + "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.4", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw=="], + + "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.11", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ=="], + + "@radix-ui/rect": ["@radix-ui/rect@1.1.3", "", {}, "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw=="], + + "@reduxjs/toolkit": ["@reduxjs/toolkit@2.13.0", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0", "immer": "^11.0.0", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "reselect": "^5.1.0" }, "peerDependencies": { "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" }, "optionalPeers": ["react", "react-redux"] }, "sha512-f6jwVRfwW30+tnllz4YcsHHpPYYOE7abcHkyUCovD+P7DyhdAI5prz2Fh/8TLhakjpN+7aIEion1s5L/tvlYlA=="], + + "@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.12", "", { "os": "android", "cpu": "arm" }, "sha512-dB/a1214qKfHMXCpgqR4OZT+jS4kTyEXbQGJPqzobt5EwH5rX080pxE37alt3RzvR1bf1Yz/yGqRfrYAxuPw0A=="], + + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.12", "", { "os": "android", "cpu": "arm64" }, "sha512-7KHFgQ5VJxIHcLlrwrc3Xbds7oTNQT7Pgi9gQCJKrd2VGab/UksIOYp6VD8MzCstGxOKMgNamPwUCfxPdP1OHg=="], + + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-3YIhqHD96nA5SaYNRBR16HnGv4oavZvXfD/ayHM+oYZ0WD/8lBAtf6zQua4kEyAvpqrluKXl0lnOBoiNby7x9w=="], + + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-UuuJ35MFw4gmFOrE9pEqIV+K3syIKveph+Qc1/ljHZVdoDW4pz/JHR/eMVom+TZGl/5OOvGJOWaOCVt3ZfqhxA=="], + + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-uMvssit0a4W+/7D8CbHUvG719mH3R2jwXAlh/XcPvuHTE0g++LymF88DCGNX0HM2rBOn0xrzgXktIB6fLSJBTQ=="], + + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.12", "", { "os": "linux", "cpu": "arm" }, "sha512-XcFu0R0xWnwzSf4IQgFH1rJIckPN1pLy2R+4r9IDB7Yfu/ys9cVqfa4pBrMHj7a3gl8mIR4nRNPg0e5IvEVs6g=="], + + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-260UrKgn8tz39ak+SMDOirKzr7V04M9dWPw5llW00SwBivCZoWcRBKV1d8cXnRkUmSZA3BdiUmBHWk7734Ulpw=="], + + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-5YK1I9SqDkbPgc1IA8BgDl34suqUS2q0KWnBrirm0E51YjOs6eo6dV6jbQfNE/argHRSvd0QUGgtpIoYx+WWpw=="], + + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Rkcrmp7eFRg74yL5fXEU91JEWbdEPLevWwGtXpmhbjlD1StScbWTmO94Bhly+Mo+ketKYkdmM1vNUKeWSlx8cQ=="], + + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-qvK4DuAsQc2BSjlx+Xr+IzOIvvxbGZqxFwdWfG6F518Erj0GGISyQbJ6pIappnOxlNPzNHvo/L0BwB30GZ+zVw=="], + + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.12", "", { "os": "linux", "cpu": "x64" }, "sha512-Q9uLBO53Xd4QIq1WOycVQyPP1O4HhraEV2qqb3uTrnVw6QZih9duY4vNXOivL1xoUS1/z+W8eF4NMfl2a8Sdjw=="], + + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.12", "", { "os": "linux", "cpu": "x64" }, "sha512-3IBxWFMjbOZskDPKv8Lf9BCnahlKuHthWkYnyIxOH/QcJrFcS4EmcenthApkwr/5+nEqZlLzeYbxeMaX7A5u4g=="], + + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xtX61xg4LKPkPWilZU1ynKClz5Gj4bf74LML4r3eVLWumKnGjoEr1OSHQhMdbBDoYTi+yjrujvpZe2pUnqCrrA=="], + + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-At7fPB6PCaIjzgIhEZFxuT+BBFqiQibJDT4d3PhiR3f4E7bbMZF4aKblbFfEM3sETRDd1YiQx/+U/g/B/ou5Ew=="], + + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.12", "", { "os": "win32", "cpu": "x64" }, "sha512-WIw2haVKwjuYdXkHaoC0mF8Le71TuCBxjrdKqLbJGctbBABj+ClfmNvtbOnzpq3RokNo5+V1qhtSzJyXorsklQ=="], + + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.64.5", "", { "os": "android", "cpu": "arm" }, "sha512-MrfgRQSpD5hkkRg6XI16B6EDLb+6+xp8xje6bpdSwkYw+qxrBKbUVrRAQu9h82UT+QumSIVQ8HXsXjPd2dlhqw=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.64.5", "", { "os": "android", "cpu": "arm64" }, "sha512-+uu2p/R9Tzn8GpoQyX1pAo5dmKT1DhXevfaCzqzmPFhnLjHLWw7uWkaVGlxmhwYoRiYYhsGfRcD5cTBpL7rgSw=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.64.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-17dys+YEmzwzzFVHQAVDONg5FALXY9kd99HFEpfk4gJVHYxoh5bGea3D4t1fXXnwkTcOe25wPyd6nWS0I8IdLQ=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.64.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-wg0zQEKkEvFZf1UaaX1UELOoPs//OSKUG7D+yITuqOsAvza7LvHTv5Wu4nmMfzNG7EYI2jp2PJC7Id6OETmsYQ=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.64.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-CoJmWcpEMuwin+TZ/ev/H9uDyF/uxUKQSWXdmIPn/UBx/uGV9+4ebMp0FwnlhWR9PE1AdzYsKf3bZevhif38+A=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.64.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-PxZnLAaTUAL5xG0W2BXLeYPKrK2b3LIASV2/e/Kf7PZvSLI1iAQIm9B9aJD+YyFIT7hc6qaMV3+OOO35R28LfA=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.64.5", "", { "os": "linux", "cpu": "arm" }, "sha512-uPo3IvnONjhbFXwWgroBeFwOicuhT7ZwQrnzkzmOtBJzeQbkUJUE1SJDfDI31ijQ5+ZUIA6sPrdUMxTSnxUWAg=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.64.5", "", { "os": "linux", "cpu": "arm" }, "sha512-ywI8Jyuc4LyHYTjC0mw4yA1ulvZBkHusdpWnoiEBsmBuHvSnt90btTBGCZ+s/2y+YNNfVAEKy33nPo5UI9zTmg=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.64.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-tmuXJoQ/3ovSr637aykZMdQteU/CB2APAz2CrvsbibuoHZi1BS1Db13cVViBdEARGwXCLn1DSJ8wsjoThLJPGA=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.64.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-5FMjSYNcUOHx7oW8Qu9KB6jGPpvF/8sG7+BDHwU/Ihh1/6FMJ9ySmsFG4tJ1qbNRuP8OLzCBDso4sF0mQaLuFQ=="], + + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.64.5", "", { "os": "linux", "cpu": "none" }, "sha512-K8ZJ+neLDbUkIrL8dqF/5LtuVCrKwo27kREgOlxjBEjlOXwHvDnHLU8Z/UKv/B49qi7e8hsSDOyoJwhwqhvX4w=="], + + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.64.5", "", { "os": "linux", "cpu": "none" }, "sha512-NlVxsq7PFPv0DBKGuNWRDKixQYQCjM53zl04mxEcKMBmJbCBIUm1JdPfIWDRHyOOFzzYPmLQzfeEwh4716ayQg=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.64.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-ARhRHJYs5eCFet5azfrHD97vOkD8OzNW2RSMlvo5CcBqXaTOLfp0OHDtUQh7/K5AL5fUYoFp2fgC9KDg4vVYMQ=="], + + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.64.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-atSgreM5C7jpxd9aEXN+6vJ5wTd05iYw+S3FiQpw7VsoEl8B90llbq35ARON+GOHj5QlVQEq2vOKWCHTob/BSQ=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.64.5", "", { "os": "linux", "cpu": "none" }, "sha512-vmlmwBFGnjVWxSRoVabNkyYuscSmg52Pd0fM8jEbNT3Jmka4A6HcB84x4uAmuumznWfBVaxKOqU6jbEgomWKCA=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.64.5", "", { "os": "linux", "cpu": "none" }, "sha512-hhJrP6S9WkbsbcHgUkvCBS5Cn8IdsG8XjlLZyxjnv9+NdoYfbbZE4TWJYr8apdaj1M5t5Y1id2StnEslxpNFyA=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.64.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-NNVCyN9pGrwFmQa4bf+Cnf5aVH3g6ZpnlOg1Cs/H9swIHT9bRixijfzUqyEIWm1WtiAuR1LJ+rJH6zpBm8kgQA=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.64.5", "", { "os": "linux", "cpu": "x64" }, "sha512-dR9nd6OKnvSS60Ifz/L7Mj5zzYyoqyRn2NwTuaOONKaa3xsVGXyp8YusMbvFyEOR94DxnwgcbgsFqkHd29ZSzQ=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.64.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1tzsMjaqhsh3bAO6SJiJqh8hIr9sSPRd+i2vPPxJHod8/XYnf9TEnK0z/u3r1z1AkI347X0KbRSg9dWcxeGahw=="], + + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.64.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-01mWMQblKoXXsmk28KXtpbA96w7g/aOWUoV3AC16fdHdqkykGalqyyM+5u8M7uP5/MEMiiPv69Hv7iv/hEH/dA=="], + + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.64.5", "", { "os": "none", "cpu": "arm64" }, "sha512-trdnMPb/DNm4V/CgsaoGgr90HfQQ071wfK0KoIs4YpLvjBgVQGXhGabg2Z6nK4EcYNilrb9ZZPcsP6+zpJIdxQ=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.64.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-x2LxZlfdfsw99T6/qb9EqE/Qi84JWzibjyOJyhJsgFJy6CafaTiqIXCCnslCGrV5XWNh7fwrS3FbRENcuKnooA=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.64.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-/Qd/9aDQIo0+jmItgBmPIs8195YVbDxRRApn47GyufgbutCDOh7eP5vSKKlI4QNFeaI+wKQAkSMsPnF3EuMpaA=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.64.5", "", { "os": "win32", "cpu": "x64" }, "sha512-ngUJ/XvOWrqbd2IMsfXfh2xXFLTP3sY7sCKvxbGvYdBZ1KmXFd3ewDZ9n3/GNzAUgzRuhLT/UvJRivbNoAQRBw=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.64.5", "", { "os": "win32", "cpu": "x64" }, "sha512-IhV6ew/DuzUWwHaYmlh7MrPCV3lKFUq/amTEwysi4r/mwNPAEfAMh5dYWeTSPTeMy2pFHGMEWYtPkHzfX3tG5A=="], + + "@scarf/scarf": ["@scarf/scarf@1.4.0", "", {}, "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ=="], + + "@shikijs/core": ["@shikijs/core@4.5.0", "", { "dependencies": { "@shikijs/primitive": "4.5.0", "@shikijs/types": "4.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.5", "hast-util-to-html": "^9.0.5" } }, "sha512-6CIgwq+bt2JZ3OSjb/nBkkwEf/CfpHcbam+Mg5Pllco7HAGRQ2/EFhZrrJtbyNTgKwknfVS+llsXGygiDGwJWw=="], + + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@4.5.0", "", { "dependencies": { "@shikijs/types": "4.5.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.6" } }, "sha512-Ed6UdEj84LyYkwnxM5CvSMWAD+id9ExTYri0/+x41U9zrgNifPSS9lZm0JqBP0VfYI8yr8tY5nbNq9+p9R0kFA=="], + + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@4.5.0", "", { "dependencies": { "@shikijs/types": "4.5.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-dKVmbJecVdB9recl8a/CU4jdpNXyjJQrIcesIkpS70s5HS02ohpnCSpMUbotPnEUzbAcLQ+S4hwZYbkBBiTcJw=="], + + "@shikijs/langs": ["@shikijs/langs@4.5.0", "", { "dependencies": { "@shikijs/types": "4.5.0" } }, "sha512-wwBTqwduXSz0X4v9Qa2EWeFaYJEw1clCncWjoRNQRbVDu3SPRTRWucnouYapKj3b8Q+sk/41Vbc4B5c8nuYzLA=="], + + "@shikijs/primitive": ["@shikijs/primitive@4.5.0", "", { "dependencies": { "@shikijs/types": "4.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.5" } }, "sha512-UM7ShlA8oz0OWxLLO8BmO9Ip6fVqdkt8yF0EH7nOCqHfrx1eEAzF0ZUVK7L+I8NEgeu1PEWSRlxaiPPeCb4iDg=="], + + "@shikijs/themes": ["@shikijs/themes@4.5.0", "", { "dependencies": { "@shikijs/types": "4.5.0" } }, "sha512-zGb87H3UBbpuI4yoh5UguB1XbU+2BDU3kflM7UfZ+Cy2hWo9lBtqjSVK20gmLY6ukYl040hAypGXMgud9vul8Q=="], + + "@shikijs/transformers": ["@shikijs/transformers@4.5.0", "", { "dependencies": { "@shikijs/core": "4.5.0", "@shikijs/types": "4.5.0" } }, "sha512-LM7twi043qCC3gRyGQfjoC2/sowzbEltWuruyRtT3RlBlk1hKzWk9ETnU7RcDBoVWQBmC8Mebnr7p28W7kPoaA=="], + + "@shikijs/types": ["@shikijs/types@4.5.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.5" } }, "sha512-drtPrtD8N0DENyD2cyt6Ajy+IWsiCLgMxODwLTpvH1UsN6a9jf90tNTPjqwHvA+0Lzu9e0txk8DjfqH1p9UJRw=="], + + "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + + "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="], + + "@streamdown/code": ["@streamdown/code@1.1.1", "", { "dependencies": { "shiki": "^3.19.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0" } }, "sha512-i7HTNuDgZWb+VdrNVOam9gQhIc5MSSDXKWXgbUrn/4vSRaSMM+Rtl10MQj4wLWPNpF7p80waJsAqFP8HZfb0Jg=="], + + "@swagger-api/apidom-ast": ["@swagger-api/apidom-ast@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-error": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", "unraw": "^3.0.0" } }, "sha512-HzudlOOzs17o2lDnDfcOFT5STKq4ckOYAK6jOaN5udDRA9luDm0axRuc3sHlImHDaOHFN+Yf+rp0zUpT+2Dsiw=="], + + "@swagger-api/apidom-core": ["@swagger-api/apidom-core@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-ast": "1.11.6", "@swagger-api/apidom-error": "1.11.6", "@types/ramda": "~0.30.0", "minim": "~0.23.8", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", "short-unique-id": "^5.3.2", "ts-mixer": "^6.0.3" } }, "sha512-OdHzm974FpfYuO0L+W5B5x+byBMjs9gWuQy37LZYjIe4K8X8IHsN00KAgODLSKgccGlnWi4QBBs8WfHNgRWqGA=="], + + "@swagger-api/apidom-error": ["@swagger-api/apidom-error@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.20.7" } }, "sha512-q44Zh9Ro0qDH/GLiFvGfe9tISSOSmq6ktrwmjuADVHwpAB//SfvhihIE9LwqNXYEZJTA4ExnFuxy01jiql++HQ=="], + + "@swagger-api/apidom-json-pointer": ["@swagger-api/apidom-json-pointer@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-error": "1.11.6", "@swaggerexpert/json-pointer": "^2.10.1" } }, "sha512-sqRsfWsTEy+NQp5Ea3xwsGlPRYnXVLcf2WXCCrexcD2OrTOxhlZ6rCZQyc2trrUwAptna/Px/czVO+TjofXf4g=="], + + "@swagger-api/apidom-ns-api-design-systems": ["@swagger-api/apidom-ns-api-design-systems@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-error": "1.11.6", "@swagger-api/apidom-ns-openapi-3-1": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", "ts-mixer": "^6.0.3" } }, "sha512-lp7YvMSXoW/UQVxOzJYCLVFIIPU2NYsT7mljIiPQTjUZAFuQ20Vi6v93YHfgVIXUj0yQYSraAnvXuO4GciWlPQ=="], + + "@swagger-api/apidom-ns-arazzo-1": ["@swagger-api/apidom-ns-arazzo-1@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-ns-json-schema-2020-12": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", "ts-mixer": "^6.0.3" } }, "sha512-GiAuYiAMwfX2AhUq4ANpmNmjJxSWiTV4QJYJUX3wHZpyU4EMXEM/BTaExGQpbmu7ewRPF4Nve/d9PKNouNXhJQ=="], + + "@swagger-api/apidom-ns-asyncapi-2": ["@swagger-api/apidom-ns-asyncapi-2@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-ns-json-schema-draft-7": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", "ts-mixer": "^6.0.3" } }, "sha512-RHTv4qINvCKe40bDyoWkhve/2/qv6GObFY0umALw2Lv1KiW2hjHrBO63Y+kU8vYu34bnhFCUDIqyCyEmJt6zkQ=="], + + "@swagger-api/apidom-ns-asyncapi-3": ["@swagger-api/apidom-ns-asyncapi-3@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-ns-asyncapi-2": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", "ts-mixer": "^6.0.3" } }, "sha512-bGUXNP+vS9bjNl8ehjmoyUck1liWjxOUQReLZG4yqIix7dHWvLSeDw1hi0pq1A+VWjixPazAcb9Y6f/t5Bqvyg=="], + + "@swagger-api/apidom-ns-json-schema-2019-09": ["@swagger-api/apidom-ns-json-schema-2019-09@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-error": "1.11.6", "@swagger-api/apidom-ns-json-schema-draft-7": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", "ts-mixer": "^6.0.4" } }, "sha512-XZmo85MS8Y5bYzrxmYbbqvXOghWZt4tInhE26c5nGbKyCx5CbsLlhzcp0wMzkb5k22BnUmlwmniVFrnveYnOGQ=="], + + "@swagger-api/apidom-ns-json-schema-2020-12": ["@swagger-api/apidom-ns-json-schema-2020-12@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-error": "1.11.6", "@swagger-api/apidom-ns-json-schema-2019-09": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", "ts-mixer": "^6.0.4" } }, "sha512-WU6Vw8aa0TLs1COGHjCFS2gz6MkQzSNZYFejs7EtZOnlAkgG3TN8H55BHVoif1QGBlrhTQ6o6RxCdk5bI1WneQ=="], + + "@swagger-api/apidom-ns-json-schema-draft-4": ["@swagger-api/apidom-ns-json-schema-draft-4@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-ast": "1.11.6", "@swagger-api/apidom-core": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", "ts-mixer": "^6.0.4" } }, "sha512-kiYyTkUCsHBKS2EFTQgI0bF0yKjOlt+QMiPzsXKzHZ7+XqMmEeRBFn5wSfTF3h9aJ5JMgtmku4j3TtdlxEUvUA=="], + + "@swagger-api/apidom-ns-json-schema-draft-6": ["@swagger-api/apidom-ns-json-schema-draft-6@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-error": "1.11.6", "@swagger-api/apidom-ns-json-schema-draft-4": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", "ts-mixer": "^6.0.4" } }, "sha512-vTlvnkmQ0UP0NeiPD8h6Kls2So8tNVruANah1n9BskJLUpvTVJV820HFDw88/+ocXUglAEg8aKENcPo0AWsrkw=="], + + "@swagger-api/apidom-ns-json-schema-draft-7": ["@swagger-api/apidom-ns-json-schema-draft-7@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-error": "1.11.6", "@swagger-api/apidom-ns-json-schema-draft-6": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", "ts-mixer": "^6.0.4" } }, "sha512-kddXboLPDceL0302icmJvltcg/LzwGPaK1W/35PiHbs0lCtgRg5bz8F0tzGbPa1zk6n2o5BSS8+YU0J6vGKf3g=="], + + "@swagger-api/apidom-ns-openapi-2": ["@swagger-api/apidom-ns-openapi-2@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-error": "1.11.6", "@swagger-api/apidom-ns-json-schema-draft-4": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", "ts-mixer": "^6.0.3" } }, "sha512-eMlB/P7apYuiwiBPsCW5zc5cyb6T/ZY7VeMSLBBN/JDy8I4rtTpFQq3ltQKXJlFhU321CuCibRiZ6p7z45wWKw=="], + + "@swagger-api/apidom-ns-openapi-3-0": ["@swagger-api/apidom-ns-openapi-3-0@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-error": "1.11.6", "@swagger-api/apidom-ns-json-schema-draft-4": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", "ts-mixer": "^6.0.3" } }, "sha512-RGngR9Daer9PtkPevSKgBqWZdXxCbDyjudadq0CImqFfjYd8OWhya20haKbPGpJuHDz7Rwmu2m92+0wZFjCcew=="], + + "@swagger-api/apidom-ns-openapi-3-1": ["@swagger-api/apidom-ns-openapi-3-1@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-ast": "1.11.6", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-json-pointer": "1.11.6", "@swagger-api/apidom-ns-json-schema-2020-12": "1.11.6", "@swagger-api/apidom-ns-openapi-3-0": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", "ts-mixer": "^6.0.3" } }, "sha512-mZe+3NgDxTjUmitxxgvlmLyK9mWSNkQajqA26j9JxcFdEvN/lcp/EsjxMX5kST4E0Qm4tQKD0i1QnJmjBSaHWw=="], + + "@swagger-api/apidom-ns-openapi-3-2": ["@swagger-api/apidom-ns-openapi-3-2@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-ast": "1.11.6", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-json-pointer": "1.11.6", "@swagger-api/apidom-ns-json-schema-2020-12": "1.11.6", "@swagger-api/apidom-ns-openapi-3-0": "1.11.6", "@swagger-api/apidom-ns-openapi-3-1": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", "ts-mixer": "^6.0.3" } }, "sha512-TBDgIDAZodphp/1DS+mTilPLfTS84e/IXGEoRCE/MFrLKa4GZFY7bx8hXQmgGJfDWwxsNcQk5Q/X+SCi+WQg0g=="], + + "@swagger-api/apidom-parser-adapter-api-design-systems-json": ["@swagger-api/apidom-parser-adapter-api-design-systems-json@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-ns-api-design-systems": "1.11.6", "@swagger-api/apidom-parser-adapter-json": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "sha512-vFwzMj6z5HBf8d8DM0eNjVkaI2hiewE4+RDkTYhpfydAUq8m8jTuzJCf7DDdQ0YdvHH7/Zwdj4EshYnzEAp8Aw=="], + + "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": ["@swagger-api/apidom-parser-adapter-api-design-systems-yaml@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-ns-api-design-systems": "1.11.6", "@swagger-api/apidom-parser-adapter-yaml-1-2": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "sha512-bwRh9F9phloYhpQJImsFuY3e8M5uf9lf436zqU413UCNnbU0F1p4HzIxKjFeIwXxiTzHfN89tCpsrPRsmLySaA=="], + + "@swagger-api/apidom-parser-adapter-arazzo-json-1": ["@swagger-api/apidom-parser-adapter-arazzo-json-1@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-ns-arazzo-1": "1.11.6", "@swagger-api/apidom-parser-adapter-json": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "sha512-opQc80CzT9buTKr3Q4k8R1DxVFJpHuJ4ej1Z6V4glGd7d2gEoabMkLKT9v9nKy50XLhJUTLkA86MLEyZPHxsfQ=="], + + "@swagger-api/apidom-parser-adapter-arazzo-yaml-1": ["@swagger-api/apidom-parser-adapter-arazzo-yaml-1@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-ns-arazzo-1": "1.11.6", "@swagger-api/apidom-parser-adapter-yaml-1-2": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "sha512-7wMLd6yGGNW0UI1ourkMfGJaR79fav1HntVCC0IvmcaO5u8X6gUIwDpqMcIuX71Ipw1FHX2x9PKSakMiWy5LJQ=="], + + "@swagger-api/apidom-parser-adapter-asyncapi-json-2": ["@swagger-api/apidom-parser-adapter-asyncapi-json-2@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-ns-asyncapi-2": "1.11.6", "@swagger-api/apidom-parser-adapter-json": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "sha512-jsx299Uconwq9oPHDNK51v3VPqm7JqkvovM2w/azW4aXQNv9u3ZcSqHMAbfC8KpYCbnx0LIuCU9OgRyH8dj8Nw=="], + + "@swagger-api/apidom-parser-adapter-asyncapi-json-3": ["@swagger-api/apidom-parser-adapter-asyncapi-json-3@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-ns-asyncapi-3": "1.11.6", "@swagger-api/apidom-parser-adapter-json": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "sha512-JePNK/AwnJk+U0yxUKDQynq6ER8Snx/zcZKxar+cBy/COMj1EV0Bif0vUdmF3RCEoPjoXNRq2HfBIbZ6yT+9AQ=="], + + "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": ["@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-ns-asyncapi-2": "1.11.6", "@swagger-api/apidom-parser-adapter-yaml-1-2": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "sha512-SJqn9y4tPSCq6dfE5hHYGpl45oLwogbXtrsbd4IWmAFrxT9VnbSUSyGmLm+bBYycQiXsvg0SJ7QRuecCeEJvfw=="], + + "@swagger-api/apidom-parser-adapter-asyncapi-yaml-3": ["@swagger-api/apidom-parser-adapter-asyncapi-yaml-3@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-ns-asyncapi-3": "1.11.6", "@swagger-api/apidom-parser-adapter-yaml-1-2": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "sha512-gTDh14ge0xBHu+44I/oBsKFC24sFK18yaL3paEdi0LGdSVKYmAbPyOaQbX3JEJ5BkOAwLvXAEQBm2snZ75JcXw=="], + + "@swagger-api/apidom-parser-adapter-json": ["@swagger-api/apidom-parser-adapter-json@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-ast": "1.11.6", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-error": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", "tree-sitter": "=0.21.1", "tree-sitter-json": "=0.24.8", "web-tree-sitter": "=0.24.5" } }, "sha512-LIbKkaydhviIJJ9VdMMmDrx96B2H21l1deH+osQt9QyrBr6Cdpo0NCv1xMr8TZi7CRjm6YD7+ec8mNmUNu54Bw=="], + + "@swagger-api/apidom-parser-adapter-openapi-json-2": ["@swagger-api/apidom-parser-adapter-openapi-json-2@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-ns-openapi-2": "1.11.6", "@swagger-api/apidom-parser-adapter-json": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "sha512-AeaRUdwEtQ1ThWQ3d47ZEH3WGLIHFR9Z5+uBhuYeT0jke0fHlLW6tnBWseZZ91Fk7Xo0sJR0fMnXRuCeBs4qTQ=="], + + "@swagger-api/apidom-parser-adapter-openapi-json-3-0": ["@swagger-api/apidom-parser-adapter-openapi-json-3-0@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-ns-openapi-3-0": "1.11.6", "@swagger-api/apidom-parser-adapter-json": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "sha512-KJ9NyuwhRc17GKMMF9IS/wFbzJ2hRNssoza16v2mVhL+xs+1qYrUi5sOwb2vCWWfNtso8Wst/ILQWxYbhFfEHw=="], + + "@swagger-api/apidom-parser-adapter-openapi-json-3-1": ["@swagger-api/apidom-parser-adapter-openapi-json-3-1@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-ns-openapi-3-1": "1.11.6", "@swagger-api/apidom-parser-adapter-json": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "sha512-9CG6vfA5usCEYf18g7+SYVRhD/zds8vvwV8gnF6gyrJOK+RbaAeVp1/Oeg499uRXy8D1LvypY07eB8aeVsaw3w=="], + + "@swagger-api/apidom-parser-adapter-openapi-json-3-2": ["@swagger-api/apidom-parser-adapter-openapi-json-3-2@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-ns-openapi-3-2": "1.11.6", "@swagger-api/apidom-parser-adapter-json": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "sha512-2EGjNq/tSFDJLVw46ApAaG2cRTxHF0Hhba5jEe5JiHTf52qZi0aPoi+YDxNnMvDKIzTrcEw34ZQddACu/JvOhQ=="], + + "@swagger-api/apidom-parser-adapter-openapi-yaml-2": ["@swagger-api/apidom-parser-adapter-openapi-yaml-2@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-ns-openapi-2": "1.11.6", "@swagger-api/apidom-parser-adapter-yaml-1-2": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "sha512-ohy7FFWM0DN0bhIqHUZJrx8qCN0qGnvjf8xqJGx/cRfxmqToEeukAXvrBBhBhtz4eqLKiSeE9TkdgHPYRRDZWg=="], + + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": ["@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-ns-openapi-3-0": "1.11.6", "@swagger-api/apidom-parser-adapter-yaml-1-2": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "sha512-6U+yTLDTdlMs68YLVKsOOpN3eFlFWeo0wbkkSSDQw7ugfpUi2PEEsjrQ+C6cviPYH3ZfZNchlj4EZaNFtMZ1/Q=="], + + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": ["@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-ns-openapi-3-1": "1.11.6", "@swagger-api/apidom-parser-adapter-yaml-1-2": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "sha512-P3t3Q4uUt92ZUrm0zPlWacPRWDHfMFqlFXdzH5dJN5phyQMMu08vdPFMaWFPv+w48wcZFXbrT/GeilorAoPlDw=="], + + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-2": ["@swagger-api/apidom-parser-adapter-openapi-yaml-3-2@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-ns-openapi-3-2": "1.11.6", "@swagger-api/apidom-parser-adapter-yaml-1-2": "1.11.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "sha512-bty6P85OHEV4YCdbVlNTsJRXZPs4ii3ynzTISURoIuyn08Y28C/Pm9H484Pl4iC9t16d1TKGngTAWc5RX0ve1Q=="], + + "@swagger-api/apidom-parser-adapter-yaml-1-2": ["@swagger-api/apidom-parser-adapter-yaml-1-2@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-ast": "1.11.6", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-error": "1.11.6", "@tree-sitter-grammars/tree-sitter-yaml": "=0.7.1", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", "tree-sitter": "=0.22.4", "web-tree-sitter": "=0.24.5" } }, "sha512-voxU8HMUypS36DYmZX8OsZ9pHz0hoT66GiBXjGKb1ETkXQR/8rVIYJnbxvVqtyXUI82aZy3dRTcXd6C2BHqH/A=="], + + "@swagger-api/apidom-reference": ["@swagger-api/apidom-reference@1.11.6", "", { "dependencies": { "@babel/runtime-corejs3": "^7.26.10", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-error": "1.11.6", "@types/ramda": "~0.30.0", "axios": "^1.18.0", "minimatch": "^10.2.6", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" }, "optionalDependencies": { "@swagger-api/apidom-json-pointer": "1.11.6", "@swagger-api/apidom-ns-arazzo-1": "1.11.6", "@swagger-api/apidom-ns-asyncapi-2": "1.11.6", "@swagger-api/apidom-ns-openapi-2": "1.11.6", "@swagger-api/apidom-ns-openapi-3-0": "1.11.6", "@swagger-api/apidom-ns-openapi-3-1": "1.11.6", "@swagger-api/apidom-ns-openapi-3-2": "1.11.6", "@swagger-api/apidom-parser-adapter-api-design-systems-json": "1.11.6", "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "1.11.6", "@swagger-api/apidom-parser-adapter-arazzo-json-1": "1.11.6", "@swagger-api/apidom-parser-adapter-arazzo-yaml-1": "1.11.6", "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "1.11.6", "@swagger-api/apidom-parser-adapter-asyncapi-json-3": "1.11.6", "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": "1.11.6", "@swagger-api/apidom-parser-adapter-asyncapi-yaml-3": "1.11.6", "@swagger-api/apidom-parser-adapter-json": "1.11.6", "@swagger-api/apidom-parser-adapter-openapi-json-2": "1.11.6", "@swagger-api/apidom-parser-adapter-openapi-json-3-0": "1.11.6", "@swagger-api/apidom-parser-adapter-openapi-json-3-1": "1.11.6", "@swagger-api/apidom-parser-adapter-openapi-json-3-2": "1.11.6", "@swagger-api/apidom-parser-adapter-openapi-yaml-2": "1.11.6", "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": "1.11.6", "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": "1.11.6", "@swagger-api/apidom-parser-adapter-openapi-yaml-3-2": "1.11.6", "@swagger-api/apidom-parser-adapter-yaml-1-2": "1.11.6" } }, "sha512-wAU/b6fBw0zS2KLvbiwY13apXbXO43N+CnlnHvGuOf4WjV+msdcb31OPUAehMENsC1BUcrf/vroQj+lJqz77dA=="], + + "@swaggerexpert/cookie": ["@swaggerexpert/cookie@2.0.2", "", { "dependencies": { "apg-lite": "^1.0.3" } }, "sha512-DPI8YJ0Vznk4CT+ekn3rcFNq1uQwvUHZhH6WvTSPD0YKBIlMS9ur2RYKghXuxxOiqOam/i4lHJH4xTIiTgs3Mg=="], + + "@swaggerexpert/json-pointer": ["@swaggerexpert/json-pointer@2.10.2", "", { "dependencies": { "apg-lite": "^1.0.4" } }, "sha512-qMx1nOrzoB+PF+pzb26Q4Tc2sOlrx9Ba2UBNX9hB31Omrq+QoZ2Gly0KLrQWw4Of1AQ4J9lnD+XOdwOdcdXqqw=="], + + "@swc/helpers": ["@swc/helpers@0.5.23", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.3.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.21.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.0" } }, "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.0", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.0", "@tailwindcss/oxide-darwin-arm64": "4.3.0", "@tailwindcss/oxide-darwin-x64": "4.3.0", "@tailwindcss/oxide-freebsd-x64": "4.3.0", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", "@tailwindcss/oxide-linux-x64-musl": "4.3.0", "@tailwindcss/oxide-wasm32-wasi": "4.3.0", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" } }, "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.0", "", { "os": "android", "cpu": "arm64" }, "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.3.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.3.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0", "", { "os": "linux", "cpu": "arm" }, "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.3.0", "", { "dependencies": { "@emnapi/core": "^1.10.0", "@emnapi/runtime": "^1.10.0", "@emnapi/wasi-threads": "^1.2.1", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.3.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA=="], + + "@tailwindcss/postcss": ["@tailwindcss/postcss@4.3.0", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.3.0", "@tailwindcss/oxide": "4.3.0", "postcss": "^8.5.10", "tailwindcss": "4.3.0" } }, "sha512-Jm05Tjx+9yCLGv5qw1c+84Psds8MnyrEQYCB+FFk2lgGiUjlRqdxke4mVTuYrj2xnVZqKim2Apr5ySuQRYAw/w=="], + + "@tailwindcss/typography": ["@tailwindcss/typography@0.5.19", "", { "dependencies": { "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg=="], + + "@tanstack/history": ["@tanstack/history@1.162.4", "", {}, "sha512-utTS5L2OkeYUzXGohL1Z8sefu1GLNOJcxe8Hd6iIdc/Xo1K1nDB2JEp4iSFhvYh33xKC9V91TxrS8qfrpoKobQ=="], + + "@tanstack/query-core": ["@tanstack/query-core@5.104.1", "", {}, "sha512-TiRghcrGkUTE+M4JBxvpPSpBhxvIzQairw+cksrHYGr+2uwC77yW1SY9nYxnYRYOppvucf8rEg6b5byoz4rTpw=="], + + "@tanstack/react-query": ["@tanstack/react-query@5.104.1", "", { "dependencies": { "@tanstack/query-core": "5.104.1" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-Zz70EgjahNI7aCz8BZxM3vUGU44ycxEAzMuCUjLeA7SVhhOkg3ZgPjPJPvHjs6Rmky/dtmnu1WzjGHNHmhCoZQ=="], + + "@tanstack/react-router": ["@tanstack/react-router@1.170.41", "", { "dependencies": { "@tanstack/history": "1.162.4", "@tanstack/react-store": "^0.11.2", "@tanstack/router-core": "1.171.34", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-Mpvw8Wm5MGDbTqI8Yc9flLKxbOHwoWocv0H2aGKcgw2a017oK5nZWqa954P+DrUx/8fI98YNywnHP5XcD3tQ9w=="], + + "@tanstack/react-store": ["@tanstack/react-store@0.11.2", "", { "dependencies": { "@tanstack/store": "0.11.2", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-oj5O5DmiMWE+ih+SioX2ayC1kbK7d3fpLx0B75fPJr9nY3zU+hoW010yr64qXsY/9fyiLVE2bn5mGNV3owm8RQ=="], + + "@tanstack/react-virtual": ["@tanstack/react-virtual@3.14.3", "", { "dependencies": { "@tanstack/virtual-core": "3.17.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-k/cnHPVaOfn46hSbiY6n4Dzf4QjCGWSF40zR5QIIYUqPAjpA6TN7InfYmcMiDVQGP2iUn9xsRbAl8u1v3UmeVQ=="], + + "@tanstack/router-core": ["@tanstack/router-core@1.171.34", "", { "dependencies": { "@tanstack/history": "1.162.4", "cookie-es": "^3.0.0", "seroval": "^1.6.7", "seroval-plugins": "^1.6.7" } }, "sha512-X+19pGuuLVPvvAvPYw9v/+ktRk5cCll8AtfpBpOhDRmVgUMrfqze0467xtq8YRVVs/mmo5rVtWn/7Tp32RDBKw=="], + + "@tanstack/store": ["@tanstack/store@0.11.2", "", {}, "sha512-sJ4mjol8uQsHV0gOJzzjwXfh2Fwm+Sz0+8deqiTm4jGbMdjzNSW+xZCFm0kUa870uhd8yi+DpKnZb5Kc4apa0Q=="], + + "@tanstack/virtual-core": ["@tanstack/virtual-core@3.17.1", "", {}, "sha512-VZyW2Uiml5tmBZwPGrSD3Sz73OxzljQMCmzYHsUTPEuTsERf5xwa+uWb01xEzkz3ZSYTjj8NEb/mKHvgKxyZdA=="], + + "@tree-sitter-grammars/tree-sitter-yaml": ["@tree-sitter-grammars/tree-sitter-yaml@0.7.1", "", { "dependencies": { "node-addon-api": "^8.3.1", "node-gyp-build": "^4.8.4" }, "peerDependencies": { "tree-sitter": "^0.22.4" }, "optionalPeers": ["tree-sitter"] }, "sha512-AynBwkIoQCTgjDR33bDUp9Mqq+YTco0is3n5hRApMqG9of/6A4eQsfC1/uSEeHSUyMQSYawcAWamsexnVpIP4Q=="], + + "@trpc/server": ["@trpc/server@11.19.0", "", { "peerDependencies": { "typescript": ">=5.7.2" }, "bin": { "intent": "bin/intent.js" } }, "sha512-7C1goiDurz82yFKtZUbEzjXd0Rg/jyisaIZQhdvus+1l7E6mwcEYZXjZ1US/HF8kOyz1LERL8UN3bcboJtjcyQ=="], + + "@types/bun": ["@types/bun@1.4.2", "", { "dependencies": { "bun-types": "1.4.2" } }, "sha512-GimotNn7+ZV0uVArItBbriZsR1oNf0+WTzPkdcFrzShI7k2norL0uzEaJT8T33dWr7O/c9ZDuAFQrctKCi72oQ=="], + + "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], + + "@types/d3": ["@types/d3@7.4.3", "", { "dependencies": { "@types/d3-array": "*", "@types/d3-axis": "*", "@types/d3-brush": "*", "@types/d3-chord": "*", "@types/d3-color": "*", "@types/d3-contour": "*", "@types/d3-delaunay": "*", "@types/d3-dispatch": "*", "@types/d3-drag": "*", "@types/d3-dsv": "*", "@types/d3-ease": "*", "@types/d3-fetch": "*", "@types/d3-force": "*", "@types/d3-format": "*", "@types/d3-geo": "*", "@types/d3-hierarchy": "*", "@types/d3-interpolate": "*", "@types/d3-path": "*", "@types/d3-polygon": "*", "@types/d3-quadtree": "*", "@types/d3-random": "*", "@types/d3-scale": "*", "@types/d3-scale-chromatic": "*", "@types/d3-selection": "*", "@types/d3-shape": "*", "@types/d3-time": "*", "@types/d3-time-format": "*", "@types/d3-timer": "*", "@types/d3-transition": "*", "@types/d3-zoom": "*" } }, "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww=="], + + "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="], + + "@types/d3-axis": ["@types/d3-axis@3.0.6", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw=="], + + "@types/d3-brush": ["@types/d3-brush@3.0.6", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A=="], + + "@types/d3-chord": ["@types/d3-chord@3.0.6", "", {}, "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg=="], + + "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="], + + "@types/d3-contour": ["@types/d3-contour@3.0.6", "", { "dependencies": { "@types/d3-array": "*", "@types/geojson": "*" } }, "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg=="], + + "@types/d3-delaunay": ["@types/d3-delaunay@6.0.4", "", {}, "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw=="], + + "@types/d3-dispatch": ["@types/d3-dispatch@3.0.7", "", {}, "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA=="], + + "@types/d3-drag": ["@types/d3-drag@3.0.7", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ=="], + + "@types/d3-dsv": ["@types/d3-dsv@3.0.7", "", {}, "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g=="], + + "@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="], + + "@types/d3-fetch": ["@types/d3-fetch@3.0.7", "", { "dependencies": { "@types/d3-dsv": "*" } }, "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA=="], + + "@types/d3-force": ["@types/d3-force@3.0.10", "", {}, "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw=="], + + "@types/d3-format": ["@types/d3-format@3.0.4", "", {}, "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g=="], + + "@types/d3-geo": ["@types/d3-geo@3.1.1", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-65Emv9fQiQQqphLlRkuQ5ypPsOmWPhtBGCMv61JDPEPMvsx+gzhGf74yw1a78xFKPj6zw4AgQICJoQv0vK9M2w=="], + + "@types/d3-hierarchy": ["@types/d3-hierarchy@3.1.7", "", {}, "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg=="], + + "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="], + + "@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="], + + "@types/d3-polygon": ["@types/d3-polygon@3.0.2", "", {}, "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA=="], + + "@types/d3-quadtree": ["@types/d3-quadtree@3.0.6", "", {}, "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg=="], + + "@types/d3-random": ["@types/d3-random@3.0.4", "", {}, "sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA=="], + + "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="], + + "@types/d3-scale-chromatic": ["@types/d3-scale-chromatic@3.1.0", "", {}, "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ=="], + + "@types/d3-selection": ["@types/d3-selection@3.0.12", "", {}, "sha512-Qe/KWYhEiIIxGs7HrAAjMfShxKldx19SJtr5zu53f3afPsdZNz7HHtdTLXo/kqeiWNXVycI24kSnfzBYkTzpgw=="], + + "@types/d3-shape": ["@types/d3-shape@3.2.0", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-kVd74ta9eof3eJOvbNd1vGKS/XERRyQbT26Og63hIsvDO84cjD5gEOhsXf26w3FSoNlPVz84DOFcKv/oou+fMw=="], + + "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="], + + "@types/d3-time-format": ["@types/d3-time-format@4.0.3", "", {}, "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg=="], + + "@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="], + + "@types/d3-transition": ["@types/d3-transition@3.0.9", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg=="], + + "@types/d3-zoom": ["@types/d3-zoom@3.0.9", "", { "dependencies": { "@types/d3-interpolate": "*", "@types/d3-selection": "*" } }, "sha512-0sE1406XBYJGiqD3AusTl9ZqC//2mIXix51tbom25gDCA8ri4xnSZg28CaSE8Srl6FClqABUKfsn0qghgdepMA=="], + + "@types/debug": ["@types/debug@4.1.13", "", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="], + + "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + + "@types/dom-speech-recognition": ["@types/dom-speech-recognition@0.0.1", "", {}, "sha512-udCxb8DvjcDKfk1WTBzDsxFbLgYxmQGKrE/ricoMqHRNjSlSUCcamVTA5lIQqzY10mY5qCY0QDwBfFEwhfoDPw=="], + + "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], + + "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="], + + "@types/geojson": ["@types/geojson@7946.0.16", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="], + + "@types/google.maps": ["@types/google.maps@3.66.6", "", {}, "sha512-F+m8NwLXXDf43/GsxmyceugqpBtKMaQGM/raqyINTG+r7fFgCXKxTZJ6Aw4QeMwUkIJwDtYVeUhndq3RFNkuDA=="], + + "@types/hast": ["@types/hast@3.0.5", "", { "dependencies": { "@types/unist": "*" } }, "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g=="], + + "@types/hogan.js": ["@types/hogan.js@3.0.5", "", {}, "sha512-/uRaY3HGPWyLqOyhgvW9Aa43BNnLZrNeQxl2p8wqId4UHMfPKolSB+U7BlZyO1ng7MkLnyEAItsBzCG0SDhqrA=="], + + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], + + "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], + + "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], + + "@types/node": ["@types/node@22.19.17", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q=="], + + "@types/prismjs": ["@types/prismjs@1.26.6", "", {}, "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw=="], + + "@types/qs": ["@types/qs@6.15.1", "", {}, "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw=="], + + "@types/ramda": ["@types/ramda@0.30.2", "", { "dependencies": { "types-ramda": "^0.30.1" } }, "sha512-PyzHvjCalm2BRYjAU6nIB3TprYwMNOUY/7P/N8bSzp9W/yM2YrtGtAnnVtaCNSeOZ8DzKyFDvaqQs7LnWwwmBA=="], + + "@types/react": ["@types/react@19.2.16", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-esJiCAnl0kfpNdE69f3So4WJUXy95dLZydX0KwK46riIHDzHM7O9Vtf9xCHW0PXIqvgqNrswl522kA/5yx+F4w=="], + + "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + + "@types/react-syntax-highlighter": ["@types/react-syntax-highlighter@15.5.13", "", { "dependencies": { "@types/react": "*" } }, "sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA=="], + + "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], + + "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + + "@types/use-sync-external-store": ["@types/use-sync-external-store@0.0.6", "", {}, "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="], + + "@uidotdev/usehooks": ["@uidotdev/usehooks@2.4.1", "", { "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" } }, "sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg=="], + + "@ungap/structured-clone": ["@ungap/structured-clone@1.4.0", "", {}, "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ=="], + + "@upsetjs/venn.js": ["@upsetjs/venn.js@2.0.0", "", { "optionalDependencies": { "d3-selection": "^3.0.0", "d3-transition": "^3.0.1" } }, "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw=="], + + "@vercel/oidc": ["@vercel/oidc@3.2.0", "", {}, "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug=="], + + "@vitest/expect": ["@vitest/expect@4.1.8", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.8", "@vitest/utils": "4.1.8", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ=="], + + "@vitest/mocker": ["@vitest/mocker@4.1.8", "", { "dependencies": { "@vitest/spy": "4.1.8", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw=="], + + "@vitest/pretty-format": ["@vitest/pretty-format@4.1.8", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA=="], + + "@vitest/runner": ["@vitest/runner@4.1.8", "", { "dependencies": { "@vitest/utils": "4.1.8", "pathe": "^2.0.3" } }, "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg=="], + + "@vitest/snapshot": ["@vitest/snapshot@4.1.8", "", { "dependencies": { "@vitest/pretty-format": "4.1.8", "@vitest/utils": "4.1.8", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ=="], + + "@vitest/spy": ["@vitest/spy@4.1.8", "", {}, "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA=="], + + "@vitest/utils": ["@vitest/utils@4.1.8", "", { "dependencies": { "@vitest/pretty-format": "4.1.8", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg=="], + + "@workflow/serde": ["@workflow/serde@4.1.0", "", {}, "sha512-pav4F2BoirECWR7Nf1TKt+2eETcBj7jj4cBefQ8VXQCA6NPkaKeLfj/zMgi+3zYV5ZIBT4GuUiphsj0/b9hPQQ=="], + + "@yuku-analyzer/binding-android-arm64": ["@yuku-analyzer/binding-android-arm64@0.10.2", "", { "os": "android", "cpu": "arm64" }, "sha512-xt4Vy3RTBw1NfszBlf4A0Rr6ZjA/th2Pj9XlWFLQHW9A1qOCtxNCoIWSzn/INtnZLMB4jO2gWdEtSaHUtK73wA=="], + + "@yuku-analyzer/binding-darwin-arm64": ["@yuku-analyzer/binding-darwin-arm64@0.10.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ftokSqvYXZxfmA/bfIbp8c1BJ8QxNax371kUOQNFyvR5hmtKqQyTqSKtMk+WaixHM3w4tKj/JdyY+ud1bGamXA=="], + + "@yuku-analyzer/binding-darwin-x64": ["@yuku-analyzer/binding-darwin-x64@0.10.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-yUrmmhmF5R4Xmbfo8gUxJKr3YbvbLcE2JvLhDLzFJkMN29/fGm6K3dZX6YpNecfgBDnQ+VywGp8K9xjRaBFZ3Q=="], + + "@yuku-analyzer/binding-freebsd-x64": ["@yuku-analyzer/binding-freebsd-x64@0.10.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-cy9+KvVoK5Xad3Vs5pLwAeZf3JC/1y0XJElgv1q6g7W7g/n52FRjedpYmQiJeMZdnhJiAshq7Vz/Xj3jC5Ns2Q=="], + + "@yuku-analyzer/binding-linux-arm-gnu": ["@yuku-analyzer/binding-linux-arm-gnu@0.10.2", "", { "os": "linux", "cpu": "arm" }, "sha512-AgVZ6VCV2XP4yHCwyv78KLV76gcu2neiEF2+RBTPQN7sqYY+bLvMxphGPmfG+9qk5Uf1PJAhZYIUnMGVbbRCZw=="], + + "@yuku-analyzer/binding-linux-arm-musl": ["@yuku-analyzer/binding-linux-arm-musl@0.10.2", "", { "os": "linux", "cpu": "arm" }, "sha512-5qAj2GjoSN6+HuH75+g5r40CFhWmMvH0K8VKWmPfQyOgA/LonoDMr5H5O5feJwzdnfdCytPDAh1RAf7/SSg7dg=="], + + "@yuku-analyzer/binding-linux-arm64-gnu": ["@yuku-analyzer/binding-linux-arm64-gnu@0.10.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-eJvQ4Ja5oPf/HOjmBkCLYyhOHUkL7U+iMAN8sX44x8exBBpPRyqnhEgTsuU73HzxPLrGPdRhybFnAE63Omib/Q=="], + + "@yuku-analyzer/binding-linux-arm64-musl": ["@yuku-analyzer/binding-linux-arm64-musl@0.10.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-TppF2j194mOQ/kBXvNr9u5TYO+eR9r8ieg4lTIEyJIVYHAR9GWeB0oFHLcJiLXVa3qUJGzE4bmzSntCW5ylA7A=="], + + "@yuku-analyzer/binding-linux-x64-gnu": ["@yuku-analyzer/binding-linux-x64-gnu@0.10.2", "", { "os": "linux", "cpu": "x64" }, "sha512-03xu6ocJuwhm1a/JKmrHiaJUO1Oj86rABsm4FAl8AvfewfW/9idIUmPiFxZfb6RfiIONegebsTGJhFx0NbBNpA=="], + + "@yuku-analyzer/binding-linux-x64-musl": ["@yuku-analyzer/binding-linux-x64-musl@0.10.2", "", { "os": "linux", "cpu": "x64" }, "sha512-f7W2sAWWQN8x8YB0RRxIche7mLImH7lDgzUy9+B69cM2I6VFJ4a5+0pgAuMLROPOqimaxFPdXqLdDE/vpgGltg=="], + + "@yuku-analyzer/binding-win32-arm64": ["@yuku-analyzer/binding-win32-arm64@0.10.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-f534qNc5YaJI29e1oryxEnxPkp7WCYJ1j7ChSec8pQNlYqTm8Tao7hij7NxRF+9BuxzeZCZ+WsbaAfLlklR0Eg=="], + + "@yuku-analyzer/binding-win32-x64": ["@yuku-analyzer/binding-win32-x64@0.10.2", "", { "os": "win32", "cpu": "x64" }, "sha512-sKHkmLK8BsindMfyIThoN0bWC5uqSJEVNaLRD/ifA99mtb0UDin1ZCg4WYSVQAmM4k2iOTl4UZx/p3zcBiBgYw=="], + + "@yuku-toolchain/types": ["@yuku-toolchain/types@0.10.2", "", {}, "sha512-sSeo4SSSToiS+sSD+bwn/s94EEcaLJ7tG5LCp8gFYC1G5VzxzX7fqB6m9RU1yMD8KTpu6zdU/I1NlQf8hVBJ9Q=="], + + "abbrev": ["abbrev@1.1.1", "", {}, "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="], + + "acorn": ["acorn@8.18.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], + + "ai": ["ai@7.0.127", "", { "dependencies": { "@ai-sdk/gateway": "4.0.103", "@ai-sdk/provider": "4.0.21", "@ai-sdk/provider-utils": "5.0.53" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-JNsNPk4ZvRGEJuqdVo2lYFkm7uAuf3p/RwKFWn1Wh/+IK3U1RyFWw2GXoFnhzqNfxEuj7XJB4WpIUhl1sHToWg=="], + + "algoliasearch": ["algoliasearch@5.53.0", "", { "dependencies": { "@algolia/abtesting": "1.19.0", "@algolia/client-abtesting": "5.53.0", "@algolia/client-analytics": "5.53.0", "@algolia/client-common": "5.53.0", "@algolia/client-insights": "5.53.0", "@algolia/client-personalization": "5.53.0", "@algolia/client-query-suggestions": "5.53.0", "@algolia/client-search": "5.53.0", "@algolia/ingestion": "1.53.0", "@algolia/monitoring": "1.53.0", "@algolia/recommend": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-OGW1q6b91CRSSeiOnM8LxuR5NYJ2esvw66jUZ4IIvdv+ItNkx3pwLuyR+jaCdbGee4ov5WgUnyPryyh11xvByQ=="], + + "algoliasearch-helper": ["algoliasearch-helper@3.29.1", "", { "dependencies": { "@algolia/events": "^4.0.1" }, "peerDependencies": { "algoliasearch": ">= 3.1 < 6" } }, "sha512-6ck2YFudF2Pje7szQoPBiRFTGfd+1I+0I/WfLPGn0bj1kvrFoOQmNyedNiDxTk3/r4IfSLDYk+RA4G7u8H6+yA=="], + + "ansi-escapes": ["ansi-escapes@7.3.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg=="], + + "ansi-regex": ["ansi-regex@6.4.0", "", {}, "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw=="], + + "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "apg-lite": ["apg-lite@1.0.5", "", {}, "sha512-SlI+nLMQDzCZfS39ihzjGp3JNBQfJXyMi6cg9tkLOCPVErgFsUIAEdO9IezR7kbP5Xd0ozcPNQBkf9TO5cHgWw=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], + + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + + "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="], + + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + + "autolinker": ["autolinker@3.16.2", "", { "dependencies": { "tslib": "^2.3.0" } }, "sha512-JiYl7j2Z19F9NdTmirENSUUIIL/9MytEWtmzhfmsKPCp9E+G35Y0UNCMoM9tFigxT59qSc8Ml2dlZXOCVTYwuA=="], + + "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], + + "axios": ["axios@1.20.0", "", { "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.6", "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg=="], + + "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], + + "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.11.27", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-ElY12DaROGuan+lMmZ8Cvo/ZUbXPe7Enc/9VU/b1T3Kp4dwytRcNdR8DoSJN5SNJT/CuvcCA0DHDVmMOCePdRQ=="], + + "before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="], + + "brace-expansion": ["brace-expansion@5.0.12", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], + + "bun-types": ["bun-types@1.4.2", "", { "dependencies": { "@types/node": "*" } }, "sha512-bxV1FgK7yBIzjRe5zBozIM4Bem11ZJcCXSrjWRG3YWLt8yFDePu4cLjpebO8OvPeIE9trbyPF4fuj3Cia4Fj3w=="], + + "call-bind": ["call-bind@1.0.9", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" } }, "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001814", "", {}, "sha512-/Uaf1lAzr59XcMpW0o96WoEfr+VXK2OX4U9AgFoiSHsVJ4HppnIFUjtYzsyDH2+tgANaQb2/oxYGwCPapN1FpA=="], + + "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + + "chai": ["chai@6.3.0", "", {}, "sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg=="], + + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + + "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], + + "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], + + "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], + + "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="], + + "chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + + "citty": ["citty@0.2.2", "", {}, "sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w=="], + + "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + + "classnames": ["classnames@2.5.1", "", {}, "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="], + + "cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], + + "cli-spinners": ["cli-spinners@3.4.0", "", {}, "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw=="], + + "cli-truncate": ["cli-truncate@5.2.0", "", { "dependencies": { "slice-ansi": "^8.0.0", "string-width": "^8.2.0" } }, "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw=="], + + "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "cmdk": ["cmdk@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "^1.1.1", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-id": "^1.1.0", "@radix-ui/react-primitive": "^2.0.2" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg=="], + + "cn": ["cn@0.3.3", "", { "bin": { "cn": "bin/cn.mjs" } }, "sha512-OEiyJkSpYm2kOuNOKQ+c2wmzZRG8orrb9fnObtHTbCGNx/E9KLnGcmcocItOzREKk5KzQN5I2e3BUCj563KEpw=="], + + "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="], + + "colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="], + + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], + + "commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], + + "compute-scroll-into-view": ["compute-scroll-into-view@3.1.1", "", {}, "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw=="], + + "content-type": ["content-type@3.1.1", "", {}, "sha512-GW4qUsfFo59d0HbUibDlWv5wPz+vAAcaTWbKIuKCf0JkC7wWkSyf8f13IpXn5JkeMlB2P8iTSSIjwXljorg2vA=="], + + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="], + + "copy-to-clipboard": ["copy-to-clipboard@3.3.3", "", { "dependencies": { "toggle-selection": "^1.0.6" } }, "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA=="], + + "core-js": ["core-js@3.50.0", "", {}, "sha512-BRWgOLKkFeCgRudR6zrs8p9XJZcE14grzKMMssoYrk6krtuEZ7MTKPIY5RzOnqsEKIR9kst7wNzphttraT+Yqw=="], + + "core-js-pure": ["core-js-pure@3.50.0", "", {}, "sha512-6GP3Pxz4IKyWjAfa747vIu/jilB5z29JWROLqH/b+pXVcpgh6tM06ZIBwSuglgVqzDYURhOK6oEzTrG0bCHitA=="], + + "cose-base": ["cose-base@1.0.3", "", { "dependencies": { "layout-base": "^1.0.0" } }, "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "css.escape": ["css.escape@1.5.1", "", {}, "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg=="], + + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "cytoscape": ["cytoscape@3.34.3", "", {}, "sha512-yfYGhRcGAntq6YBD583j4n0Eg3jIxvWmZtz/5uz9UYkeIStSlMxuUja+ec5j3iBD8nv1rwaOAYMW09tBdkSeaQ=="], + + "cytoscape-cose-bilkent": ["cytoscape-cose-bilkent@4.1.0", "", { "dependencies": { "cose-base": "^1.0.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ=="], + + "cytoscape-fcose": ["cytoscape-fcose@2.2.0", "", { "dependencies": { "cose-base": "^2.2.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ=="], + + "d3": ["d3@7.9.0", "", { "dependencies": { "d3-array": "3", "d3-axis": "3", "d3-brush": "3", "d3-chord": "3", "d3-color": "3", "d3-contour": "4", "d3-delaunay": "6", "d3-dispatch": "3", "d3-drag": "3", "d3-dsv": "3", "d3-ease": "3", "d3-fetch": "3", "d3-force": "3", "d3-format": "3", "d3-geo": "3", "d3-hierarchy": "3", "d3-interpolate": "3", "d3-path": "3", "d3-polygon": "3", "d3-quadtree": "3", "d3-random": "3", "d3-scale": "4", "d3-scale-chromatic": "3", "d3-selection": "3", "d3-shape": "3", "d3-time": "3", "d3-time-format": "4", "d3-timer": "3", "d3-transition": "3", "d3-zoom": "3" } }, "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA=="], + + "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="], + + "d3-axis": ["d3-axis@3.0.0", "", {}, "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw=="], + + "d3-brush": ["d3-brush@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "3", "d3-transition": "3" } }, "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ=="], + + "d3-chord": ["d3-chord@3.0.1", "", { "dependencies": { "d3-path": "1 - 3" } }, "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g=="], + + "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="], + + "d3-contour": ["d3-contour@4.0.2", "", { "dependencies": { "d3-array": "^3.2.0" } }, "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA=="], + + "d3-delaunay": ["d3-delaunay@6.0.4", "", { "dependencies": { "delaunator": "5" } }, "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A=="], + + "d3-dispatch": ["d3-dispatch@3.0.1", "", {}, "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="], + + "d3-drag": ["d3-drag@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-selection": "3" } }, "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg=="], + + "d3-dsv": ["d3-dsv@3.0.1", "", { "dependencies": { "commander": "7", "iconv-lite": "0.6", "rw": "1" }, "bin": { "csv2json": "bin/dsv2json.js", "csv2tsv": "bin/dsv2dsv.js", "dsv2dsv": "bin/dsv2dsv.js", "dsv2json": "bin/dsv2json.js", "json2csv": "bin/json2dsv.js", "json2dsv": "bin/json2dsv.js", "json2tsv": "bin/json2dsv.js", "tsv2csv": "bin/dsv2dsv.js", "tsv2json": "bin/dsv2json.js" } }, "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q=="], + + "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="], + + "d3-fetch": ["d3-fetch@3.0.1", "", { "dependencies": { "d3-dsv": "1 - 3" } }, "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw=="], + + "d3-force": ["d3-force@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-quadtree": "1 - 3", "d3-timer": "1 - 3" } }, "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg=="], + + "d3-format": ["d3-format@3.1.2", "", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="], + + "d3-geo": ["d3-geo@3.1.1", "", { "dependencies": { "d3-array": "2.5.0 - 3" } }, "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q=="], + + "d3-hierarchy": ["d3-hierarchy@3.1.2", "", {}, "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA=="], + + "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="], + + "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="], + + "d3-polygon": ["d3-polygon@3.0.1", "", {}, "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg=="], + + "d3-quadtree": ["d3-quadtree@3.0.1", "", {}, "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw=="], + + "d3-random": ["d3-random@3.0.1", "", {}, "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ=="], + + "d3-sankey": ["d3-sankey@0.12.3", "", { "dependencies": { "d3-array": "1 - 2", "d3-shape": "^1.2.0" } }, "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ=="], + + "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="], + + "d3-scale-chromatic": ["d3-scale-chromatic@3.1.0", "", { "dependencies": { "d3-color": "1 - 3", "d3-interpolate": "1 - 3" } }, "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ=="], + + "d3-selection": ["d3-selection@3.0.0", "", {}, "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="], + + "d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="], + + "d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="], + + "d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="], + + "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="], + + "d3-transition": ["d3-transition@3.0.1", "", { "dependencies": { "d3-color": "1 - 3", "d3-dispatch": "1 - 3", "d3-ease": "1 - 3", "d3-interpolate": "1 - 3", "d3-timer": "1 - 3" }, "peerDependencies": { "d3-selection": "2 - 3" } }, "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w=="], + + "d3-zoom": ["d3-zoom@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "2 - 3", "d3-transition": "2 - 3" } }, "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw=="], + + "dagre-d3-es": ["dagre-d3-es@7.0.14", "", { "dependencies": { "d3": "^7.9.0", "lodash-es": "^4.17.21" } }, "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg=="], + + "date-fns": ["date-fns@4.4.0", "", {}, "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w=="], + + "dayjs": ["dayjs@1.11.23", "", {}, "sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "decimal.js-light": ["decimal.js-light@2.5.1", "", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="], + + "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], + + "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], + + "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], + + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], + + "delaunator": ["delaunator@5.1.0", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ=="], + + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], + + "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], + + "dompurify": ["dompurify@3.4.16", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-sqo+pNp3qRhCIpbgRi1y8Tgk27Bo2Ry7w0dC1NBeNTdZChWjz9Xb/KOoZbRP/R6pQZ80Qw8YhXw13hWWBbMRnQ=="], + + "dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="], + + "drange": ["drange@1.1.1", "", {}, "sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + + "enhanced-resolve": ["enhanced-resolve@5.26.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw=="], + + "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + + "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-module-lexer": ["es-module-lexer@2.3.2", "", {}, "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw=="], + + "es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "es-toolkit": ["es-toolkit@1.52.0", "", {}, "sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA=="], + + "esast-util-from-estree": ["esast-util-from-estree@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "unist-util-position-from-estree": "^2.0.0" } }, "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ=="], + + "esast-util-from-js": ["esast-util-from-js@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "acorn": "^8.0.0", "esast-util-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw=="], + + "esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="], + + "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "estree-util-attach-comments": ["estree-util-attach-comments@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw=="], + + "estree-util-build-jsx": ["estree-util-build-jsx@3.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-walker": "^3.0.0" } }, "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ=="], + + "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="], + + "estree-util-scope": ["estree-util-scope@1.0.1", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0" } }, "sha512-B0np3dcdxqILX5e9nEi5/Fr4K7gL4oYFVPV1zRa2e9wRCbQoZZNWOZFYyoInvXUPJXBXjss+QXlWLJChDEHDkA=="], + + "estree-util-to-js": ["estree-util-to-js@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "astring": "^1.8.0", "source-map": "^0.7.0" } }, "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg=="], + + "estree-util-value-to-estree": ["estree-util-value-to-estree@3.5.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-aMV56R27Gv3QmfmF1MY12GWkGzzeAezAX+UplqHVASfjc9wNzI/X6hC0S9oxq61WT4aQesLGslWP9tKk6ghRZQ=="], + + "estree-util-visit": ["estree-util-visit@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/unist": "^3.0.0" } }, "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww=="], + + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="], + + "eventsource-parser": ["eventsource-parser@3.1.1", "", {}, "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ=="], + + "expect-type": ["expect-type@1.4.0", "", {}, "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA=="], + + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fast-json-patch": ["fast-json-patch@3.1.1", "", {}, "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ=="], + + "fastdom": ["fastdom@1.0.12", "", { "dependencies": { "strictdom": "^1.0.1" } }, "sha512-LB+xjSTEbjHE1cWsxu+tN2Xqr1kpi+V9aADI7sVM5ZMaXyYGPHULQMzpJMYqOTULK/73pUkWVzzObFRBkPr+hg=="], + + "fastq": ["fastq@1.20.3", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw=="], + + "fault": ["fault@1.0.4", "", { "dependencies": { "format": "^0.2.0" } }, "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "fflate": ["fflate@0.4.9", "", {}, "sha512-zdxgIEddhfsyCaWpJ2SdXEP8ZMrKJ6+5jl4OupODcywU0IhRk6gdXuVGcPICyfx2H97hVK7xmJtRLPjkxAX8Vw=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "follow-redirects": ["follow-redirects@1.16.1", "", { "peerDependencies": { "debug": "*" }, "optionalPeers": ["debug"] }, "sha512-FNvFGzoMLWmE6Yj9spb/zjd7yiNCHiAW9/Tg9CXrQ8wuu32HtlJOwWO11OJafl5FfY3DxTdQ0vj42zU1kvv5jg=="], + + "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], + + "form-data": ["form-data@4.0.6", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.4", "mime-types": "^2.1.35" } }, "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ=="], + + "format": ["format@0.2.2", "", {}, "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="], + + "framer-motion": ["framer-motion@12.43.0", "", { "dependencies": { "motion-dom": "^12.43.0", "motion-utils": "^12.39.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-1eaL3RvR/kAlbG7UYcpMptEyzPoENO0c6w7ZnB3/hh2vSAz/6uGAFn6fdoqTBguNstf3MsFhJHsD/0DHiclG+g=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "fumadocs-core": ["fumadocs-core@16.15.18", "", { "dependencies": { "@fumari/image-size": "^0.1.1", "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.1", "zbsearch": "^4.0.0" }, "peerDependencies": { "@mdx-js/mdx": "*", "@mixedbread/sdk": "0.x.x", "@modelcontextprotocol/server": "2.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": "*", "meilisearch": ">=0.50.0 <1.0.0", "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" }, "optionalPeers": ["@mdx-js/mdx", "@mixedbread/sdk", "@modelcontextprotocol/server", "@orama/core", "@oramacloud/client", "@tanstack/react-router", "@types/estree-jsx", "@types/hast", "@types/mdast", "@types/react", "algoliasearch", "flexsearch", "lucide-react", "meilisearch", "next", "react", "react-dom", "react-router", "waku", "zod"] }, "sha512-w5VV92FbtBJHIBdTtb/UgUMwALseE6D98Rr/ErV8eCPdtQcjP9ttR8IcjuV6C+JTc1NSSOZBK4CAUNhDIU8MIA=="], + + "fumadocs-mdx": ["fumadocs-mdx@15.4.6", "", { "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", "github-slugger": "^2.0.0", "magic-string": "^1.4.1", "mdast-util-mdx": "^3.0.0", "picocolors": "^1.1.1", "picomatch": "^4.0.7", "tinyexec": "^1.3.1", "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.1", "yuku-analyzer": "^0.10.2", "zod": "^4.6.5" }, "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" }, "optionalPeers": ["@fumadocs/satteri", "@types/mdast", "@types/mdx", "@types/react", "mdast-util-directive", "next", "react", "rolldown", "satteri", "vite"], "bin": { "fumadocs-mdx": "./bin.js" } }, "sha512-R8bdonO7+vDplod2YOLohH8cqr6ALVD/oaURObRoh+aKKyWeC5pTia2KkDNzOoUnkQLpTS9hTsaf5zt5RqMHUA=="], + + "fumadocs-ui": ["fumadocs-ui@16.15.18", "", { "dependencies": { "@fuma-translate/react": "^1.0.2", "@fumadocs/tailwind": "0.1.2", "@radix-ui/react-accordion": "^1.2.20", "@radix-ui/react-collapsible": "^1.1.20", "@radix-ui/react-dialog": "^1.1.23", "@radix-ui/react-direction": "^1.1.4", "@radix-ui/react-navigation-menu": "^1.2.22", "@radix-ui/react-popover": "^1.1.23", "@radix-ui/react-presence": "^1.1.10", "@radix-ui/react-scroll-area": "^1.2.18", "@radix-ui/react-slot": "^1.3.3", "@radix-ui/react-tabs": "^1.1.21", "class-variance-authority": "^0.7.1", "cn": "^0.3.0", "lucide-react": "^1.47.0", "motion": "^13.4.0", "next-themes": "^0.4.6", "react-remove-scroll": "^2.7.2", "rehype-raw": "^7.0.0", "scroll-into-view-if-needed": "^3.1.0", "shiki": "^4.4.3", "unist-util-visit": "^5.1.0" }, "peerDependencies": { "@types/mdx": "*", "@types/react": "*", "fumadocs-core": "16.15.18", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "takumi-js": "*" }, "optionalPeers": ["@types/mdx", "@types/react", "next", "takumi-js"] }, "sha512-cRgFwYsdjnDJIt5m+3+CQdnP+1iKgvbVLhKQtJfNLz9ieE6PtenaU9CGMQMvOFEQQDTPDh9K60jxA/DQ3uLa1w=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "get-east-asian-width": ["get-east-asian-width@1.7.0", "", {}, "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], + + "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="], + + "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], + + "hast-util-from-parse5": ["hast-util-from-parse5@8.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "hastscript": "^9.0.0", "property-information": "^7.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" } }, "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg=="], + + "hast-util-parse-selector": ["hast-util-parse-selector@4.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A=="], + + "hast-util-raw": ["hast-util-raw@9.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "hast-util-from-parse5": "^8.0.0", "hast-util-to-parse5": "^8.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "parse5": "^7.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw=="], + + "hast-util-sanitize": ["hast-util-sanitize@5.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "unist-util-position": "^5.0.0" } }, "sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg=="], + + "hast-util-to-estree": ["hast-util-to-estree@3.1.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-attach-comments": "^3.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w=="], + + "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], + + "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="], + + "hast-util-to-parse5": ["hast-util-to-parse5@8.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA=="], + + "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], + + "hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], + + "highlight.js": ["highlight.js@10.7.3", "", {}, "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="], + + "highlightjs-vue": ["highlightjs-vue@1.0.0", "", {}, "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA=="], + + "hogan.js": ["hogan.js@3.0.2", "", { "dependencies": { "mkdirp": "0.3.0", "nopt": "1.0.10" }, "bin": { "hulk": "./bin/hulk" } }, "sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg=="], + + "htm": ["htm@3.1.1", "", {}, "sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ=="], + + "html-url-attributes": ["html-url-attributes@3.0.1", "", {}, "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ=="], + + "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], + + "https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], + + "husky": ["husky@9.1.7", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="], + + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + + "immer": ["immer@11.1.21", "", {}, "sha512-bZSadeCPGdaY5/qRLgr6mDSH1ra21rD3rqpiWk7+QTr4ayYPTE9qZYdLh/NdPhMe0wSrTvHD/Lk8q5EwhUs0aw=="], + + "immutable": ["immutable@4.3.9", "", {}, "sha512-ObHy4YN7ycwZOUCLI1/6svfyAFu7vL8RhAvVu/bh/RZW9EPlOyDaQ9jDQWCtdqzaXUjgXZCW1migtHE7YI7UGQ=="], + + "import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="], + + "instantsearch-ui-components": ["instantsearch-ui-components@0.29.0", "", { "dependencies": { "@swc/helpers": "0.5.18", "markdown-to-jsx": "^7.7.15" } }, "sha512-/7bY63BM+nRs38vL/1hYtjheDlf3FEK2edsnc3UEA3JJZZZk0Bq1Ds0o8WcSVymT/BSZz8DNb87eLDTz6gDVcw=="], + + "instantsearch.js": ["instantsearch.js@4.100.0", "", { "dependencies": { "@algolia/events": "^4.0.1", "@swc/helpers": "0.5.18", "@types/dom-speech-recognition": "^0.0.1", "@types/google.maps": "^3.55.12", "@types/hogan.js": "^3.0.0", "@types/qs": "^6.5.3", "algoliasearch-helper": "3.29.1", "hogan.js": "^3.0.2", "htm": "^3.0.0", "instantsearch-ui-components": "0.29.0", "preact": "^10.10.0", "qs": "^6.5.1", "react": ">= 0.14.0", "search-insights": "^2.17.2" }, "peerDependencies": { "algoliasearch": ">= 3.1 < 6" } }, "sha512-8fomIg5pwSTDDQQL/yUNWdc0lvvw+1xhjgNlWJwgmRsonZ3GlLT00bvznfrTHq0IRw9/Z894uMQEpH0t/l742A=="], + + "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], + + "invariant": ["invariant@2.2.4", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA=="], + + "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], + + "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="], + + "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], + + "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="], + + "is-interactive": ["is-interactive@2.0.0", "", {}, "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], + + "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], + + "is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], + + "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], + + "isbot": ["isbot@5.2.2", "", {}, "sha512-iQcBXcd+Rv/pkubRyGh2utW2j1oPG5hZY6TUhVPpqK4G+o3IbxpJNx04hgksjc/N7GK5pEorUxDeg31cFgEk/w=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], + + "js-file-download": ["js-file-download@0.4.12", "", {}, "sha512-rML+NkoD08p5Dllpjo0ffy4jRHeY6Zsapvr/W86N7E0yuzAO6qa5X9+xog6zQNlH102J7IXljNY2FtS6Lj3ucg=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.3.2", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA=="], + + "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], + + "json-schema-to-ts": ["json-schema-to-ts@3.1.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="], + + "json-with-bigint": ["json-with-bigint@3.5.12", "", {}, "sha512-uwbF/wSSuOgC7qqlq27Xp5B6a2MHVug3t0idZdTqu0JnlFvgJuH7ju+KAk/J06C7GfhoYy2gnb9wz2INqcne7w=="], + + "jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="], + + "katex": ["katex@0.16.47", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg=="], + + "khroma": ["khroma@2.1.0", "", {}, "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="], + + "layout-base": ["layout-base@1.0.2", "", {}, "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg=="], + + "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "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" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + + "lint-staged": ["lint-staged@16.4.0", "", { "dependencies": { "commander": "^14.0.3", "listr2": "^9.0.5", "picomatch": "^4.0.3", "string-argv": "^0.3.2", "tinyexec": "^1.0.4", "yaml": "^2.8.2" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw=="], + + "listr2": ["listr2@9.0.5", "", { "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g=="], + + "lodash": ["lodash@4.18.1", "", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="], + + "lodash-es": ["lodash-es@4.18.1", "", {}, "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A=="], + + "lodash.debounce": ["lodash.debounce@4.0.8", "", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="], + + "log-symbols": ["log-symbols@7.0.1", "", { "dependencies": { "is-unicode-supported": "^2.0.0", "yoctocolors": "^2.1.1" } }, "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg=="], + + "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="], + + "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], + + "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + + "lowlight": ["lowlight@1.20.0", "", { "dependencies": { "fault": "^1.0.0", "highlight.js": "~10.7.0" } }, "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw=="], + + "lucide-react": ["lucide-react@0.577.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "markdown-extensions": ["markdown-extensions@2.0.0", "", {}, "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q=="], + + "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], + + "markdown-to-jsx": ["markdown-to-jsx@7.7.17", "", { "peerDependencies": { "react": ">= 0.14.0" }, "optionalPeers": ["react"] }, "sha512-7mG/1feQ0TX5I7YyMZVDgCC/y2I3CiEhIRQIhyov9nGBP5eoVrOXXHuL5ZP8GRfxVZKRiXWJgwXkb9It+nQZfQ=="], + + "marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], + + "mdast-util-from-markdown": ["mdast-util-from-markdown@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-KRLgmPaP2H1ul2bpoJkOS+9pAcDAAj9Nu1mqLnh8NLeJG5tlvj42TH80PIGBpROWlRXLk0lDrWNuZCFy/tsB4A=="], + + "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="], + + "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="], + + "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="], + + "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-OuJHqvr455pwu2OaOrir7dbzqs4jlWKOtlu9L6GjcIjjQwNyw02VntQsr/Ek6/A13vgnXfUhBekWRUP1OkNivw=="], + + "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="], + + "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="], + + "mdast-util-mdx": ["mdast-util-mdx@3.0.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w=="], + + "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="], + + "mdast-util-mdx-jsx": ["mdast-util-mdx-jsx@3.2.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" } }, "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q=="], + + "mdast-util-mdxjs-esm": ["mdast-util-mdxjs-esm@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg=="], + + "mdast-util-newline-to-break": ["mdast-util-newline-to-break@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-find-and-replace": "^3.0.0" } }, "sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog=="], + + "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="], + + "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="], + + "mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-character": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-wgyJtgUkUcdU7zci7uuwc/tzoAhm0TswWhaXuvnWolqug+8jY7bgNkBBL0dYBnHgc/tHL7lCzRdPsdUxUKKBqw=="], + + "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "mermaid": ["mermaid@11.17.2", "", { "dependencies": { "@braintree/sanitize-url": "^7.1.2", "@iconify/utils": "^3.0.2", "@mermaid-js/parser": "^1.2.1", "@types/d3": "^7.4.3", "@upsetjs/venn.js": "^2.0.0", "cytoscape": "^3.34.0", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.14", "dayjs": "^1.11.21", "dompurify": "^3.3.3", "es-toolkit": "^1.45.1", "fastdom": "1.0.12", "katex": "^0.16.47", "khroma": "^2.1.0", "marked": "^16.3.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0" } }, "sha512-V6K3C8EBdEsPFZXSKMJe6ppQOENxuHARr9GvHX4hh47lAbhMRD9qf4oEK7LoaRQxULMa80/qt5gHO73aCleBBg=="], + + "micromark": ["micromark@4.0.3", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-edit-map": "^1.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oGYfQzHSG5dOMovQcJ3fyTmZlWAWpi0XA0sJwJs+i6OT88o1+Jtw/8z0CmdowSLxGhhFK89rQ/oXph/wN02PNw=="], + + "micromark-core-commonmark": ["micromark-core-commonmark@2.0.4", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.1.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-edit-map": "^1.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.3" } }, "sha512-wxEeE8v8XVvOrxn1TZj74qYhAtTQsSqufHVS3uNVyT1MupXxhyaHk8/9xDNPh9BmL77QNDgWTkZ3RsYHB2ry7w=="], + + "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], + + "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], + + "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="], + + "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="], + + "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.2", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-pRzm4kDTu0MjlmBkxmS9yYhw60nncfcEwu9NNdPFSQEFXS95ZKyIIyTSHu/o3ReBUrLKYEq+7YaXCRn/bPB4MA=="], + + "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="], + + "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="], + + "micromark-extension-mdx-expression": ["micromark-extension-mdx-expression@3.0.1", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q=="], + + "micromark-extension-mdx-jsx": ["micromark-extension-mdx-jsx@3.0.2", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ=="], + + "micromark-extension-mdx-md": ["micromark-extension-mdx-md@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ=="], + + "micromark-extension-mdxjs": ["micromark-extension-mdxjs@3.0.0", "", { "dependencies": { "acorn": "^8.0.0", "acorn-jsx": "^5.0.0", "micromark-extension-mdx-expression": "^3.0.0", "micromark-extension-mdx-jsx": "^3.0.0", "micromark-extension-mdx-md": "^2.0.0", "micromark-extension-mdxjs-esm": "^3.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ=="], + + "micromark-extension-mdxjs-esm": ["micromark-extension-mdxjs-esm@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-position-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A=="], + + "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="], + + "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="], + + "micromark-factory-mdx-expression": ["micromark-factory-mdx-expression@2.0.3", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-position-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ=="], + + "micromark-factory-space": ["micromark-factory-space@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-fS8hnLIjnjvdQIj39Geug8wWsR0HrYZ43KShKxNfwT7t2LOHo/LbWZEzEaSOjwtjdCsjoE6syHhonEzW0zv0+Q=="], + + "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="], + + "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="], + + "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], + + "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="], + + "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="], + + "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="], + + "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="], + + "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="], + + "micromark-util-edit-map": ["micromark-util-edit-map@1.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-Pa2ljlsEL6sVwFaYeyrOLSYbQt73JvGbPOYFq+9AElXiSnfI5Q4465RRZ1sHv7lDjDOnGRDDaqPXqClqWK/q1Q=="], + + "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], + + "micromark-util-events-to-acorn": ["micromark-util-events-to-acorn@2.0.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg=="], + + "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="], + + "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="], + + "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="], + + "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], + + "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="], + + "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], + + "micromark-util-types": ["micromark-util-types@2.0.3", "", {}, "sha512-oxB2Ik03hI0gv+VNn9tnh1t1YEe9MDPptViAEgfdf3YQHsn0pzGTgCdlSCJXcwhqm8phaEuM7zeEu3QQzVBrPg=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], + + "minim": ["minim@0.23.8", "", { "dependencies": { "lodash": "^4.15.0" } }, "sha512-bjdr2xW1dBCMsMGGsUeqM4eFI60m94+szhxWys+B1ztIt6gWSfeGBdSVCIawezeHYLYn0j6zrsXdQS/JllBzww=="], + + "minimatch": ["minimatch@10.2.6", "", { "dependencies": { "brace-expansion": "^5.0.8" } }, "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A=="], + + "mkdirp": ["mkdirp@0.3.0", "", {}, "sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew=="], + + "motion": ["motion@12.40.0", "", { "dependencies": { "framer-motion": "^12.40.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-yjrHUrBFW6kQvjJwRsoiPSAhC5tRwRqNGJWmiJ4CrGnbKp0V88AdzkhBmDoqIsIPfarOe0Uddd37Xq43/gIocA=="], + + "motion-dom": ["motion-dom@12.43.0", "", { "dependencies": { "motion-utils": "^12.39.0" } }, "sha512-azKON4d9S65PEoFUiQTMTgPheEmzf2QngdRc50AKfJp9Q9mmcBVw22c8eMq9k8kxOFHdL7+WZY7N/5F/lwiDag=="], + + "motion-utils": ["motion-utils@12.39.0", "", {}, "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nanoid": ["nanoid@3.3.19", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug=="], + + "neotraverse": ["neotraverse@1.0.1", "", {}, "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w=="], + + "next": ["next@16.3.8", "", { "dependencies": { "@next/env": "16.3.8", "@swc/helpers": "0.5.23", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.5.23", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.3.8", "@next/swc-darwin-x64": "16.3.8", "@next/swc-linux-arm64-gnu": "16.3.8", "@next/swc-linux-arm64-musl": "16.3.8", "@next/swc-linux-x64-gnu": "16.3.8", "@next/swc-linux-x64-musl": "16.3.8", "@next/swc-win32-arm64-msvc": "16.3.8", "@next/swc-win32-x64-msvc": "16.3.8", "sharp": "^0.35.4" }, "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" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-U7QEZaTini6wKrb8A8hqLLqYQyCetegKjCpJOyxk642vWoMoU1x5PyZCJFvgYgiptA8xc5j/9xYlZFO7w9Sjmw=="], + + "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], + + "next-validate-link": ["next-validate-link@1.6.6", "", { "dependencies": { "js-yaml": "^4.1.1", "picocolors": "^1.1.1", "remark": "^15.0.1", "remark-gfm": "^4.0.1", "remark-mdx": "^3.1.1", "tinyglobby": "^0.2.16", "unified": "^11.0.5", "unist-util-visit": "^5.1.0" }, "peerDependencies": { "@react-router/dev": "7.x.x", "@types/mdast": "*", "@types/unist": "*" }, "optionalPeers": ["@react-router/dev", "@types/mdast", "@types/unist"] }, "sha512-Ep96TQ2zkaVi/ekByURk1fT75xymRfai+SCPEjX/ld64AA+dMjqhsDxERbdDIKONZGcHTypEE27fOY7eyMZIdw=="], + + "node-abort-controller": ["node-abort-controller@3.1.1", "", {}, "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ=="], + + "node-addon-api": ["node-addon-api@8.9.2", "", {}, "sha512-VijLXbi3UACN69I0JVXJsX4tjACjNoQDgv2gTF6sx2wWEi8tkSg2eX8p5gSIFi8z2+DL3oHmY6OyKce38SDolg=="], + + "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="], + + "nopt": ["nopt@1.0.10", "", { "dependencies": { "abbrev": "1" }, "bin": { "nopt": "./bin/nopt.js" } }, "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg=="], + + "npm-to-yarn": ["npm-to-yarn@3.2.0", "", {}, "sha512-K1HmQeZT2HrjpsR6KgqbN2FAXL2NrJJmNUSD9ck7HGTVu1JKXox8n9SB+tjbU8m8JGLF4OscrroPepew/L7/Xw=="], + + "nypm": ["nypm@0.6.10", "", { "dependencies": { "citty": "^0.2.2", "pathe": "^2.0.3", "tinyexec": "^1.3.1" }, "bin": { "nypm": "./dist/cli.mjs" } }, "sha512-W72Hrj1petq+b3Hk2aAC+9zswetlIFTnDW4s5djseZh2nYqBbyQLOtj472HwcbcWykPBUW1WpWpjOd4nK6gMRw=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "obug": ["obug@2.2.1", "", {}, "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q=="], + + "onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], + + "oniguruma-parser": ["oniguruma-parser@0.12.2", "", {}, "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw=="], + + "oniguruma-to-es": ["oniguruma-to-es@4.3.6", "", { "dependencies": { "oniguruma-parser": "^0.12.2", "regex": "^6.1.0", "regex-recursion": "^6.0.2" } }, "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA=="], + + "openai": ["openai@6.41.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"] }, "sha512-IGWPopZq6Rjoynjfb3NSLf/z2MTw7UiOsm9TAjPGAjUESH7Uq41Trg4QWehBEn58p74i+m7uoRPV2vXcpPXhyA=="], + + "openapi-path-templating": ["openapi-path-templating@2.2.1", "", { "dependencies": { "apg-lite": "^1.0.4" } }, "sha512-eN14VrDvl/YyGxxrkGOHkVkWEoPyhyeydOUrbvjoz8K5eIGgELASwN1eqFOJ2CTQMGCy2EntOK1KdtJ8ZMekcg=="], + + "openapi-server-url-templating": ["openapi-server-url-templating@1.3.0", "", { "dependencies": { "apg-lite": "^1.0.4" } }, "sha512-DPlCms3KKEbjVQb0spV6Awfn6UWNheuG/+folQPzh/wUaKwuqvj8zt5gagD7qoyxtE03cIiKPgLFS3Q8Bz00uQ=="], + + "ora": ["ora@9.4.0", "", { "dependencies": { "chalk": "^5.6.2", "cli-cursor": "^5.0.0", "cli-spinners": "^3.2.0", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.1.0", "log-symbols": "^7.0.1", "stdin-discarder": "^0.3.2", "string-width": "^8.1.0" } }, "sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ=="], + + "package-manager-detector": ["package-manager-detector@1.8.0", "", {}, "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A=="], + + "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], + + "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + + "path-data-parser": ["path-data-parser@0.1.0", "", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="], + + "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], + + "points-on-curve": ["points-on-curve@0.2.0", "", {}, "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A=="], + + "points-on-path": ["points-on-path@0.2.1", "", { "dependencies": { "path-data-parser": "0.1.0", "points-on-curve": "0.2.0" } }, "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g=="], + + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], + + "postcss": ["postcss@8.5.26", "", { "dependencies": { "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ=="], + + "postcss-selector-parser": ["postcss-selector-parser@6.0.10", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w=="], + + "posthog-js": ["posthog-js@1.379.0", "", { "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/api-logs": "^0.208.0", "@opentelemetry/exporter-logs-otlp-http": "^0.208.0", "@opentelemetry/resources": "^2.2.0", "@opentelemetry/sdk-logs": "^0.208.0", "@posthog/core": "1.30.3", "@posthog/types": "1.379.0", "core-js": "^3.38.1", "dompurify": "^3.3.2", "fflate": "^0.4.8", "preact": "^10.28.2", "query-selector-shadow-dom": "^1.0.1", "web-vitals": "^5.1.0" } }, "sha512-cSESmnPvIaY/pyhV5a6pF00q8tqmXlJBQWX9T7dbBh9TXhVM5oQVi7g77vWGD9G8wq4iNF5ZOxqeAK9dpOSOYg=="], + + "posthog-node": ["posthog-node@5.35.12", "", { "dependencies": { "@posthog/core": "1.30.3" }, "peerDependencies": { "rxjs": "^7.0.0" }, "optionalPeers": ["rxjs"] }, "sha512-WLbeUdSDpERDENBgaDahMo9sqwjo8Gc1f1dnM01HciwdP8OnGlbcVs8zrqjz6Z9GvBnTnRTa0Q2bBQeXrBIeHg=="], + + "preact": ["preact@10.29.8", "", { "peerDependencies": { "preact-render-to-string": ">=5" }, "optionalPeers": ["preact-render-to-string"] }, "sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q=="], + + "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="], + + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], + + "property-information": ["property-information@7.2.0", "", {}, "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg=="], + + "protobufjs": ["protobufjs@7.6.6", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.5", "@protobufjs/eventemitter": "^1.1.1", "@protobufjs/fetch": "^1.1.1", "@protobufjs/float": "^1.0.2", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", "long": "^5.3.2" } }, "sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg=="], + + "proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="], + + "qs": ["qs@6.16.0", "", { "dependencies": { "es-define-property": "^1.0.1", "side-channel": "^1.1.1" } }, "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA=="], + + "query-selector-shadow-dom": ["query-selector-shadow-dom@1.0.1", "", {}, "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw=="], + + "querystringify": ["querystringify@2.2.0", "", {}, "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "ramda": ["ramda@0.30.1", "", {}, "sha512-tEF5I22zJnuclswcZMc8bDIrwRHRzf+NqVEmqg50ShAZMP7MWeR/RGDthfM/p+BlqvF2fXAzpn8i+SJcYD3alw=="], + + "ramda-adjunct": ["ramda-adjunct@5.1.0", "", { "peerDependencies": { "ramda": ">= 0.30.0" } }, "sha512-8qCpl2vZBXEJyNbi4zqcgdfHtcdsWjOGbiNSEnEBrM6Y0OKOT8UxJbIVGm1TIcjaSu2MxaWcgtsNlKlCk7o7qg=="], + + "randexp": ["randexp@0.5.3", "", { "dependencies": { "drange": "^1.0.2", "ret": "^0.2.0" } }, "sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w=="], + + "randombytes": ["randombytes@2.1.0", "", { "dependencies": { "safe-buffer": "^5.1.0" } }, "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ=="], + + "react": ["react@19.2.7", "", {}, "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ=="], + + "react-copy-to-clipboard": ["react-copy-to-clipboard@5.1.1", "", { "dependencies": { "copy-to-clipboard": "^3.3.3", "prop-types": "^15.8.1" }, "peerDependencies": { "react": ">=15.3.0" } }, "sha512-s+HrzLyJBxrpGTYXF15dTgMjAJpEPZT/Yp6NytAtZMRngejxt6Pt5WrfFxLAcsqUDU6sY1Jz6tyHwIicE1U2Xg=="], + + "react-day-picker": ["react-day-picker@10.0.1", "", { "dependencies": { "@date-fns/tz": "^1.4.1", "date-fns": "^4.1.0" }, "peerDependencies": { "@types/react": ">=16.8.0", "react": ">=16.8.0" }, "optionalPeers": ["@types/react"] }, "sha512-eNh6BlwcYInWaJtRv18mXQ06Ys/H6rdTZAnTaSdOYJuTpwP1JMCHNd1FDRadA+gbeinq+psdULN5Xnowy9mV8w=="], + + "react-debounce-input": ["react-debounce-input@3.3.0", "", { "dependencies": { "lodash.debounce": "^4", "prop-types": "^15.8.1" }, "peerDependencies": { "react": "^15.3.0 || 16 || 17 || 18" } }, "sha512-VEqkvs8JvY/IIZvh71Z0TC+mdbxERvYF33RcebnodlsUZ8RSgyKe2VWaHXv4+/8aoOgXLxWrdsYs2hDhcwbUgA=="], + + "react-dom": ["react-dom@19.2.7", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.7" } }, "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ=="], + + "react-hook-form": ["react-hook-form@7.77.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-Sslh9YDYc0GDlWT/lxasnIduNo4v3yyvqRGvmGKUre5AFjDs/HV9/OafHGD8d+sB2yoL4UIL9L8X9i0WlZZebg=="], + + "react-immutable-proptypes": ["react-immutable-proptypes@2.2.0", "", { "dependencies": { "invariant": "^2.2.2" }, "peerDependencies": { "immutable": ">=3.6.2" } }, "sha512-Vf4gBsePlwdGvSZoLSBfd4HAP93HDauMY4fDjXhreg/vg6F3Fj/MXDNyTbltPC/xZKmZc+cjLu3598DdYK6sgQ=="], + + "react-immutable-pure-component": ["react-immutable-pure-component@2.2.2", "", { "peerDependencies": { "immutable": ">= 2 || >= 4.0.0-rc", "react": ">= 16.6", "react-dom": ">= 16.6" } }, "sha512-vkgoMJUDqHZfXXnjVlG3keCxSO/U6WeDQ5/Sl0GK2cH8TOxEzQ5jXqDXHEL/jqk6fsNxV05oH5kD7VNMUE2k+A=="], + + "react-inspector": ["react-inspector@6.0.2", "", { "peerDependencies": { "react": "^16.8.4 || ^17.0.0 || ^18.0.0" } }, "sha512-x+b7LxhmHXjHoU/VrFAzw5iutsILRoYyDq97EDYdFpPLcvqtEzk4ZSZSQjnFPbr5T57tLXnHcqFYoN1pI6u8uQ=="], + + "react-instantsearch": ["react-instantsearch@7.34.0", "", { "dependencies": { "@swc/helpers": "0.5.18", "instantsearch-ui-components": "0.29.0", "instantsearch.js": "4.100.0", "react-instantsearch-core": "7.34.0" }, "peerDependencies": { "algoliasearch": ">= 3.1 < 6", "react": ">= 16.8.0 < 20", "react-dom": ">= 16.8.0 < 20" } }, "sha512-qQ2tKQUyxluyb9ACjXXKiLzAnlcyELPuLMmkF/gCQXe3Sr7SKKhoabW+oEQHTNsBQBeOKRnxKGiLbECZ+VyKzw=="], + + "react-instantsearch-core": ["react-instantsearch-core@7.34.0", "", { "dependencies": { "@swc/helpers": "0.5.18", "algoliasearch-helper": "3.29.1", "instantsearch.js": "4.100.0", "use-sync-external-store": "^1.0.0" }, "peerDependencies": { "algoliasearch": ">= 3.1 < 6", "react": ">= 16.8.0 < 20" } }, "sha512-TnQdiIR44hHn/3Ef187Bu/bnTKxwk/Wi6zyDgft0pcXfjjGu4/7Y8X1FA27akXU20XzK3R+E2uizjOB50EBdRQ=="], + + "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "react-markdown": ["react-markdown@10.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ=="], + + "react-redux": ["react-redux@9.3.0", "", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "@types/react": "^18.2.25 || ^19", "react": "^18.0 || ^19", "redux": "^5.0.0" }, "optionalPeers": ["@types/react", "redux"] }, "sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g=="], + + "react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="], + + "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], + + "react-resizable-panels": ["react-resizable-panels@4.11.2", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-+kfFbDZ8mygc7g0vxOcDzCVGuwiIUOnILqPoUHo6/uP+Mmyx6HzZU+kj1aOPDlktXuobYbr6BtQekvJwHRX4Eg=="], + + "react-shiki": ["react-shiki@0.10.1", "", { "dependencies": { "clsx": "^2.1.1", "dequal": "^2.0.3", "hast-util-to-jsx-runtime": "^2.3.6", "shiki": "^4.0.2", "unist-util-visit": "^5.1.0" }, "peerDependencies": { "@types/react": ">=16.14.0", "@types/react-dom": ">=16.14.0", "react": ">=16.14.0", "react-dom": ">=16.14.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-mQ6CXwQ6mEIsQvK30+HRONZsiUKGAOubaVCfXCe0Ejsp7P6U1QDpayOHyAkcMTzSLvgfPnCkny399EK7Mb46Sg=="], + + "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], + + "react-syntax-highlighter": ["react-syntax-highlighter@16.1.1", "", { "dependencies": { "@babel/runtime": "^7.28.4", "highlight.js": "^10.4.1", "highlightjs-vue": "^1.0.0", "lowlight": "^1.17.0", "prismjs": "^1.30.0", "refractor": "^5.0.0" }, "peerDependencies": { "react": ">= 0.14.0" } }, "sha512-PjVawBGy80C6YbC5DDZJeUjBmC7skaoEUdvfFQediQHgCL7aKyVHe57SaJGfQsloGDac+gCpTfRdtxzWWKmCXA=="], + + "readdirp": ["readdirp@5.1.1", "", {}, "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA=="], + + "recharts": ["recharts@3.10.1", "", { "dependencies": { "@reduxjs/toolkit": "^1.9.0 || 2.x.x", "clsx": "^2.1.1", "decimal.js-light": "^2.5.1", "es-toolkit": "^1.39.3", "eventemitter3": "^5.0.1", "immer": "^11.1.8", "react-redux": "8.x.x || 9.x.x", "reselect": "5.2.0", "tiny-invariant": "^1.3.3", "use-sync-external-store": "^1.2.2", "victory-vendor": "^37.0.2" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-QXFrvt6IVcw7eeZCoyXTwkIJAX3Dv1nyVhMicXJ47GsGDDpcN8z6o644DibE9XjpBTThtsomLKnTV6lc+cVFUA=="], + + "recma-build-jsx": ["recma-build-jsx@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-build-jsx": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew=="], + + "recma-jsx": ["recma-jsx@1.0.1", "", { "dependencies": { "acorn-jsx": "^5.0.0", "estree-util-to-js": "^2.0.0", "recma-parse": "^1.0.0", "recma-stringify": "^1.0.0", "unified": "^11.0.0" }, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w=="], + + "recma-parse": ["recma-parse@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "esast-util-from-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ=="], + + "recma-stringify": ["recma-stringify@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-to-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g=="], + + "redux": ["redux@5.0.1", "", {}, "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="], + + "redux-immutable": ["redux-immutable@4.0.0", "", { "peerDependencies": { "immutable": "^3.8.1 || ^4.0.0-rc.1" } }, "sha512-SchSn/DWfGb3oAejd+1hhHx01xUoxY+V7TeK0BKqpkLKiQPVFf7DYzEaKmrEVxsWxielKfSK9/Xq66YyxgR1cg=="], + + "redux-thunk": ["redux-thunk@3.1.0", "", { "peerDependencies": { "redux": "^5.0.0" } }, "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw=="], + + "refractor": ["refractor@5.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/prismjs": "^1.0.0", "hastscript": "^9.0.0", "parse-entities": "^4.0.0" } }, "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw=="], + + "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="], + + "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="], + + "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], + + "rehype-harden": ["rehype-harden@1.1.8", "", { "dependencies": { "unist-util-visit": "^5.0.0" } }, "sha512-Qn7vR1xrf6fZCrkm9TDWi/AB4ylrHy+jqsNm1EHOAmbARYA6gsnVJBq/sdBh6kmT4NEZxH5vgIjrscefJAOXcw=="], + + "rehype-raw": ["rehype-raw@7.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-raw": "^9.0.0", "vfile": "^6.0.0" } }, "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww=="], + + "rehype-recma": ["rehype-recma@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "hast-util-to-estree": "^3.0.0" } }, "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw=="], + + "rehype-sanitize": ["rehype-sanitize@6.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-sanitize": "^5.0.0" } }, "sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg=="], + + "remark": ["remark@15.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A=="], + + "remark-breaks": ["remark-breaks@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-newline-to-break": "^2.0.0", "unified": "^11.0.0" } }, "sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ=="], + + "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], + + "remark-mdx": ["remark-mdx@3.1.1", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg=="], + + "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], + + "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="], + + "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], + + "remarkable": ["remarkable@2.0.1", "", { "dependencies": { "argparse": "^1.0.10", "autolinker": "^3.11.0" }, "bin": { "remarkable": "bin/remarkable.js" } }, "sha512-YJyMcOH5lrR+kZdmB0aJJ4+93bEojRZ1HGDn9Eagu6ibg7aVZhc3OWbbShRid+Q5eAfsEqWxpe+g5W5nYNfNiA=="], + + "remend": ["remend@1.3.0", "", {}, "sha512-iIhggPkhW3hFImKtB10w0dz4EZbs28mV/dmbcYVonWEJ6UGHHpP+bFZnTh6GNWJONg5m+U56JrL+8IxZRdgWjw=="], + + "repeat-string": ["repeat-string@1.6.1", "", {}, "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w=="], + + "requires-port": ["requires-port@1.0.0", "", {}, "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="], + + "reselect": ["reselect@5.3.0", "", {}, "sha512-XGoLeRAVzUTcJ1qkxPQhDJyIZ5d6zzZD9nT7AEZOaaU9UbWclhycElmhO+VD5bFeLuzhPBaOV2oXC8uG35ZSpg=="], + + "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], + + "ret": ["ret@0.2.2", "", {}, "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], + + "robust-predicates": ["robust-predicates@3.0.3", "", {}, "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA=="], + + "rolldown": ["rolldown@1.2.12", "", { "dependencies": { "@oxc-project/types": "=0.152.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm-eabi": "1.2.12", "@rolldown/binding-android-arm64": "1.2.12", "@rolldown/binding-darwin-arm64": "1.2.12", "@rolldown/binding-darwin-x64": "1.2.12", "@rolldown/binding-freebsd-x64": "1.2.12", "@rolldown/binding-linux-arm-gnueabihf": "1.2.12", "@rolldown/binding-linux-arm64-gnu": "1.2.12", "@rolldown/binding-linux-arm64-musl": "1.2.12", "@rolldown/binding-linux-ppc64-gnu": "1.2.12", "@rolldown/binding-linux-s390x-gnu": "1.2.12", "@rolldown/binding-linux-x64-gnu": "1.2.12", "@rolldown/binding-linux-x64-musl": "1.2.12", "@rolldown/binding-openharmony-arm64": "1.2.12", "@rolldown/binding-win32-arm64-msvc": "1.2.12", "@rolldown/binding-win32-x64-msvc": "1.2.12" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-8wafseiaG80xmXSfqidUNqZcylTlhmPZZt+za2m+js2sFZ8dTNlhIOV2WcbIPx2hgwPBJpEUGFAMZ9bgBBLTSQ=="], + + "rollup": ["rollup@4.64.5", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@napi-rs/lzma-linux-x64-gnu": "1.5.1", "@rollup/rollup-android-arm-eabi": "4.64.5", "@rollup/rollup-android-arm64": "4.64.5", "@rollup/rollup-darwin-arm64": "4.64.5", "@rollup/rollup-darwin-x64": "4.64.5", "@rollup/rollup-freebsd-arm64": "4.64.5", "@rollup/rollup-freebsd-x64": "4.64.5", "@rollup/rollup-linux-arm-gnueabihf": "4.64.5", "@rollup/rollup-linux-arm-musleabihf": "4.64.5", "@rollup/rollup-linux-arm64-gnu": "4.64.5", "@rollup/rollup-linux-arm64-musl": "4.64.5", "@rollup/rollup-linux-loong64-gnu": "4.64.5", "@rollup/rollup-linux-loong64-musl": "4.64.5", "@rollup/rollup-linux-ppc64-gnu": "4.64.5", "@rollup/rollup-linux-ppc64-musl": "4.64.5", "@rollup/rollup-linux-riscv64-gnu": "4.64.5", "@rollup/rollup-linux-riscv64-musl": "4.64.5", "@rollup/rollup-linux-s390x-gnu": "4.64.5", "@rollup/rollup-linux-x64-gnu": "4.64.5", "@rollup/rollup-linux-x64-musl": "4.64.5", "@rollup/rollup-openbsd-x64": "4.64.5", "@rollup/rollup-openharmony-arm64": "4.64.5", "@rollup/rollup-win32-arm64-msvc": "4.64.5", "@rollup/rollup-win32-ia32-msvc": "4.64.5", "@rollup/rollup-win32-x64-gnu": "4.64.5", "@rollup/rollup-win32-x64-msvc": "4.64.5", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-EAP5ZHcz2ijbgCWwPkpI7zh6bfsri2Ez5FtCy2dCp1k62duXzfG7DLGGh7RU2QQXWwavT+u4qpvDF/TWOiiGTw=="], + + "roughjs": ["roughjs@4.6.6", "", { "dependencies": { "hachure-fill": "^0.5.2", "path-data-parser": "^0.1.0", "points-on-curve": "^0.2.0", "points-on-path": "^0.2.1" } }, "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + + "scroll-into-view-if-needed": ["scroll-into-view-if-needed@3.1.0", "", { "dependencies": { "compute-scroll-into-view": "^3.0.2" } }, "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ=="], + + "search-insights": ["search-insights@2.17.3", "", {}, "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ=="], + + "semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], + + "serialize-error": ["serialize-error@8.1.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ=="], + + "seroval": ["seroval@1.6.8", "", {}, "sha512-HlSgSAkTk4EqHcje1ptJjfZi1YDv5KbhVJ/d3P7T/nAXua2VmDu+AKDX5VTdFfZf48nDkWB2TKYt0DrCSa+3wg=="], + + "seroval-plugins": ["seroval-plugins@1.6.8", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-N7mWAMydj89EnYTHtRpS3LBrAz3J9O6oVrqpuXuvlAHfAUG8WEKqtbAd7SBiJNY5NfCAFhSdyAh7wI4Shjqy4g=="], + + "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], + + "sha.js": ["sha.js@2.4.12", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1", "to-buffer": "^1.2.0" }, "bin": { "sha.js": "bin.js" } }, "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w=="], + + "sharp": ["sharp@0.35.5", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.5", "@img/sharp-darwin-x64": "0.35.5", "@img/sharp-freebsd-wasm32": "0.35.5", "@img/sharp-libvips-darwin-arm64": "1.3.4", "@img/sharp-libvips-darwin-x64": "1.3.4", "@img/sharp-libvips-linux-arm": "1.3.4", "@img/sharp-libvips-linux-arm64": "1.3.4", "@img/sharp-libvips-linux-ppc64": "1.3.4", "@img/sharp-libvips-linux-riscv64": "1.3.4", "@img/sharp-libvips-linux-s390x": "1.3.4", "@img/sharp-libvips-linux-x64": "1.3.4", "@img/sharp-libvips-linuxmusl-arm64": "1.3.4", "@img/sharp-libvips-linuxmusl-x64": "1.3.4", "@img/sharp-linux-arm": "0.35.5", "@img/sharp-linux-arm64": "0.35.5", "@img/sharp-linux-ppc64": "0.35.5", "@img/sharp-linux-riscv64": "0.35.5", "@img/sharp-linux-s390x": "0.35.5", "@img/sharp-linux-x64": "0.35.5", "@img/sharp-linuxmusl-arm64": "0.35.5", "@img/sharp-linuxmusl-x64": "0.35.5", "@img/sharp-webcontainers-wasm32": "0.35.5", "@img/sharp-win32-arm64": "0.35.5", "@img/sharp-win32-ia32": "0.35.5", "@img/sharp-win32-x64": "0.35.5" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "shiki": ["shiki@4.5.0", "", { "dependencies": { "@shikijs/core": "4.5.0", "@shikijs/engine-javascript": "4.5.0", "@shikijs/engine-oniguruma": "4.5.0", "@shikijs/langs": "4.5.0", "@shikijs/themes": "4.5.0", "@shikijs/types": "4.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.5" } }, "sha512-OcRW72ngQvc+MSVWBRIovC/BTW589iUE/Vxav7VeM4RcY9MUjqB+IR6ILG1k9nuW6LiG/nct9uma4/MBtnZbmQ=="], + + "short-unique-id": ["short-unique-id@5.3.2", "", { "bin": { "short-unique-id": "bin/short-unique-id", "suid": "bin/short-unique-id" } }, "sha512-KRT/hufMSxXKEDSQujfVE0Faa/kZ51ihUcZQAcmP04t00DvPj7Ox5anHke1sJYUtzSuiT/Y5uyzg/W7bBEGhCg=="], + + "side-channel": ["side-channel@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ=="], + + "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], + + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], + + "slice-ansi": ["slice-ansi@8.0.0", "", { "dependencies": { "ansi-styles": "^6.2.3", "is-fullwidth-code-point": "^5.1.0" } }, "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg=="], + + "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], + + "source-map-js": ["source-map-js@1.2.2", "", {}, "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw=="], + + "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + + "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], + + "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], + + "std-env": ["std-env@4.3.0", "", {}, "sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag=="], + + "stdin-discarder": ["stdin-discarder@0.3.2", "", {}, "sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A=="], + + "streamdown": ["streamdown@2.5.0", "", { "dependencies": { "clsx": "^2.1.1", "hast-util-to-jsx-runtime": "^2.3.6", "html-url-attributes": "^3.0.1", "marked": "^17.0.1", "mermaid": "^11.12.2", "rehype-harden": "^1.1.8", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remend": "1.3.0", "tailwind-merge": "^3.4.0", "unified": "^11.0.5", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-/tTnURfIOxZK/pqJAxsfCvETG/XCJHoWnk3jq9xLcuz6CSpnjjuxSRBTTL4PKGhxiZQf0lqPxGhImdpwcZ2XwA=="], + + "strictdom": ["strictdom@1.0.1", "", {}, "sha512-cEmp9QeXXRmjj/rVp9oyiqcvyocWab/HaoN4+bwFeZ7QzykJD6L3yD4v12K1x0tHpqRqVpJevN3gW7kyM39Bqg=="], + + "string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="], + + "string-width": ["string-width@8.3.0", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ=="], + + "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], + + "strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + + "style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="], + + "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="], + + "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "@babel/core": "*", "babel-plugin-macros": "*", "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" }, "optionalPeers": ["@babel/core", "babel-plugin-macros"] }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], + + "stylis": ["stylis@4.4.0", "", {}, "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA=="], + + "swagger-client": ["swagger-client@3.38.2", "", { "dependencies": { "@babel/runtime-corejs3": "^7.22.15", "@scarf/scarf": "=1.4.0", "@swagger-api/apidom-core": "1.11.6", "@swagger-api/apidom-error": "1.11.6", "@swagger-api/apidom-json-pointer": "1.11.6", "@swagger-api/apidom-ns-openapi-3-1": "1.11.6", "@swagger-api/apidom-ns-openapi-3-2": "1.11.6", "@swagger-api/apidom-reference": "1.11.6", "@swaggerexpert/cookie": "^2.0.2", "deepmerge": "~4.3.0", "fast-json-patch": "^3.0.0-1", "js-yaml": "^4.3.2", "neotraverse": "=1.0.1", "node-abort-controller": "^3.1.1", "openapi-path-templating": "^2.2.1", "openapi-server-url-templating": "^1.3.0", "ramda": "^0.30.1", "ramda-adjunct": "^5.1.0" }, "optionalDependencies": { "@swagger-api/apidom-ns-asyncapi-2": "1.11.6", "@swagger-api/apidom-ns-asyncapi-3": "1.11.6", "@swagger-api/apidom-ns-openapi-2": "1.11.6", "@swagger-api/apidom-parser-adapter-api-design-systems-json": "1.11.6", "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "1.11.6", "@swagger-api/apidom-parser-adapter-arazzo-json-1": "1.11.6", "@swagger-api/apidom-parser-adapter-arazzo-yaml-1": "1.11.6", "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "1.11.6", "@swagger-api/apidom-parser-adapter-asyncapi-json-3": "1.11.6", "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": "1.11.6", "@swagger-api/apidom-parser-adapter-asyncapi-yaml-3": "1.11.6", "@swagger-api/apidom-parser-adapter-json": "1.11.6", "@swagger-api/apidom-parser-adapter-openapi-json-2": "1.11.6", "@swagger-api/apidom-parser-adapter-openapi-json-3-0": "1.11.6", "@swagger-api/apidom-parser-adapter-openapi-json-3-1": "1.11.6", "@swagger-api/apidom-parser-adapter-openapi-json-3-2": "1.11.6", "@swagger-api/apidom-parser-adapter-openapi-yaml-2": "1.11.6", "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": "1.11.6", "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": "1.11.6", "@swagger-api/apidom-parser-adapter-openapi-yaml-3-2": "1.11.6", "@swagger-api/apidom-parser-adapter-yaml-1-2": "1.11.6" } }, "sha512-MrMYADBRg7nY5Y+CY9E76/Zm2LSghhln4wNNU6jGn3bNZ76BRQ3hQRyCHCyJK9tVgTDUmYCcYgYWrwGruL1mIw=="], + + "swagger-ui-react": ["swagger-ui-react@5.32.14", "", { "dependencies": { "@babel/runtime-corejs3": "^7.27.1", "@scarf/scarf": "=1.4.0", "base64-js": "^1.5.1", "buffer": "^6.0.3", "classnames": "^2.5.1", "css.escape": "1.5.1", "deep-extend": "0.6.0", "dompurify": "^3.4.13", "ieee754": "^1.2.1", "immutable": "^4.3.9", "js-file-download": "^0.4.12", "js-yaml": "=4.3.1", "lodash": "^4.18.1", "prop-types": "^15.8.1", "randexp": "^0.5.3", "randombytes": "^2.1.0", "react-copy-to-clipboard": "5.1.1", "react-debounce-input": "=3.3.0", "react-immutable-proptypes": "2.2.0", "react-immutable-pure-component": "^2.2.0", "react-inspector": "^6.0.1", "react-redux": "^9.3.0", "react-syntax-highlighter": "^16.0.0", "redux": "^5.0.1", "redux-immutable": "^4.0.0", "remarkable": "^2.0.1", "reselect": "^5.1.1", "serialize-error": "^8.1.0", "sha.js": "^2.4.12", "swagger-client": "^3.38.0", "url-parse": "^1.5.10", "xml": "=1.0.1", "xml-but-prettier": "^1.0.1", "zenscroll": "^4.0.2" }, "peerDependencies": { "react": ">=16.8.0 <20", "react-dom": ">=16.8.0 <20" } }, "sha512-6LAVBeC78DplbJ7kutm/YeBYo22nPzGOca4bIZAvQG4w2eSetnYDdazaUfY0qzQUlg/H90HnYZX3rg67EmENOw=="], + + "swr": ["swr@2.5.1", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-BRw55e8r0B7SpDN20CAzoQAHl7y1yP7/Zt7oqUjMv0vSt2u2Xnkm88Ws+VypbV9BXHQVuSuyVq7zMjO16wSExw=="], + + "tailwind-merge": ["tailwind-merge@3.6.0", "", {}, "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w=="], + + "tailwindcss": ["tailwindcss@4.3.0", "", {}, "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q=="], + + "tailwindcss-animate": ["tailwindcss-animate@1.0.7", "", { "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders" } }, "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA=="], + + "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], + + "third-party-capital": ["third-party-capital@1.0.20", "", {}, "sha512-oB7yIimd8SuGptespDAZnNkzIz+NWaJCu2RMsbs4Wmp9zSDUM8Nhi3s2OOcqYuv3mN4hitXc8DVx+LyUmbUDiA=="], + + "throttleit": ["throttleit@2.1.0", "", {}, "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw=="], + + "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], + + "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + + "tinyexec": ["tinyexec@1.3.1", "", {}, "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA=="], + + "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], + + "tinyrainbow": ["tinyrainbow@3.2.0", "", {}, "sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A=="], + + "to-buffer": ["to-buffer@1.2.2", "", { "dependencies": { "isarray": "^2.0.5", "safe-buffer": "^5.2.1", "typed-array-buffer": "^1.0.3" } }, "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "toggle-selection": ["toggle-selection@1.0.6", "", {}, "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ=="], + + "tree-sitter": ["tree-sitter@0.21.1", "", { "dependencies": { "node-addon-api": "^8.0.0", "node-gyp-build": "^4.8.0" } }, "sha512-7dxoA6kYvtgWw80265MyqJlkRl4yawIjO7S5MigytjELkX43fV2WsAXzsNfO7sBpPPCF5Gp0+XzHk0DwLCq3xQ=="], + + "tree-sitter-json": ["tree-sitter-json@0.24.8", "", { "dependencies": { "node-addon-api": "^8.2.2", "node-gyp-build": "^4.8.2" }, "peerDependencies": { "tree-sitter": "^0.21.1" }, "optionalPeers": ["tree-sitter"] }, "sha512-Tc9ZZYwHyWZ3Tt1VEw7Pa2scu1YO7/d2BCBbKTx5hXwig3UfdQjsOPkPyLpDJOn/m1UBEWYAtSdGAwCSyagBqQ=="], + + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], + + "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], + + "trpc-cli": ["trpc-cli@0.12.4", "", { "dependencies": { "commander": "^14.0.0" }, "peerDependencies": { "@orpc/server": "^1.0.0", "@trpc/server": "^10.45.2 || ^11.0.1", "@valibot/to-json-schema": "^1.1.0", "effect": "^3.14.2 || ^4.0.0", "valibot": "^1.1.0", "zod": "^3.24.0 || ^4.0.0" }, "optionalPeers": ["@orpc/server", "@trpc/server", "@valibot/to-json-schema", "effect", "valibot", "zod"], "bin": { "trpc-cli": "dist/bin.js" } }, "sha512-Yo2Ob5J7hUZSWZ2A1M9Kb+0qfSxwmcmYIs3kkQyLd3sn0qU4ryGzNsySfrY3+urqp6FnDnIIdbCSqC9BKxK6Ag=="], + + "ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="], + + "ts-dedent": ["ts-dedent@2.3.0", "", {}, "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg=="], + + "ts-mixer": ["ts-mixer@6.0.4", "", {}, "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="], + + "ts-toolbelt": ["ts-toolbelt@9.6.0", "", {}, "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "tsx": ["tsx@4.23.15", "", { "dependencies": { "esbuild": "~0.28.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw=="], + + "type-fest": ["type-fest@0.20.2", "", {}, "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="], + + "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], + + "types-ramda": ["types-ramda@0.30.1", "", { "dependencies": { "ts-toolbelt": "^9.6.0" } }, "sha512-1HTsf5/QVRmLzcGfldPFvkVsAdi1db1BBKzi7iW3KBUlOICg/nKnFS+jGqDJS3YD8VsWbAh7JiHeBvbsw8RPxA=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "ultracite": ["ultracite@6.1.0", "", { "dependencies": { "@clack/prompts": "^0.11.0", "@trpc/server": "^11.6.0", "deepmerge": "^4.3.1", "jsonc-parser": "^3.3.1", "nypm": "^0.6.2", "trpc-cli": "^0.12.0", "zod": "^4.1.12" }, "bin": { "ultracite": "dist/index.js" } }, "sha512-CHdtZbG9MNIcIZ7fCB3SlNbaeH5tmEmOjKThjqongviMsJY5uFxtNBSQhYbj/ExX81mehp1QY2NVKkTiC4CNVA=="], + + "undici": ["undici@7.30.0", "", {}, "sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], + + "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], + + "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], + + "unist-util-position-from-estree": ["unist-util-position-from-estree@2.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ=="], + + "unist-util-remove-position": ["unist-util-remove-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q=="], + + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], + + "unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="], + + "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], + + "universal-user-agent": ["universal-user-agent@7.0.3", "", {}, "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A=="], + + "unraw": ["unraw@3.0.0", "", {}, "sha512-08/DA66UF65OlpUDIQtbJyrqTR0jTAlJ+jsnkQ4jxR7+K5g5YG1APZKQSMCE1vqqmD+2pv6+IdEjmopFatacvg=="], + + "url-parse": ["url-parse@1.5.10", "", { "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" } }, "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ=="], + + "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], + + "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], + + "use-stick-to-bottom": ["use-stick-to-bottom@1.1.6", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-z3Up8jYQGTkUCsGBnwg6/wj70KgXoW5Kz1AAc1j8MtQuYMBo6ZsdhrIXoegxa7gaMMilgQYyTohTrt3p94jHog=="], + + "use-sync-external-store": ["use-sync-external-store@1.7.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-6L+EeigHMQhdaIPNIFUKwfWJSwWFQ8gJbJ2DLOs5sDIegTwR9fRxvnM3uciHKjIZhFz+KAv2emhWMRvDmMcY8A=="], + + "usehooks-ts": ["usehooks-ts@3.1.1", "", { "dependencies": { "lodash.debounce": "^4.0.8" }, "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "uuid": ["uuid@14.0.2", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-xZe/16rV4aa+HGSOCiY2YeLT1OybRLrrkL/Rqaq7p7GMVXjFh+6wN4oMYgjFmnSnhY8t6Xpdl2l9qmnHYuMHwQ=="], + + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], + + "vfile-location": ["vfile-location@5.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg=="], + + "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], + + "victory-vendor": ["victory-vendor@37.3.6", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ=="], + + "vite": ["vite@7.3.5", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww=="], + + "vitest": ["vitest@4.1.8", "", { "dependencies": { "@vitest/expect": "4.1.8", "@vitest/mocker": "4.1.8", "@vitest/pretty-format": "4.1.8", "@vitest/runner": "4.1.8", "@vitest/snapshot": "4.1.8", "@vitest/spy": "4.1.8", "@vitest/utils": "4.1.8", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.8", "@vitest/browser-preview": "4.1.8", "@vitest/browser-webdriverio": "4.1.8", "@vitest/coverage-istanbul": "4.1.8", "@vitest/coverage-v8": "4.1.8", "@vitest/ui": "4.1.8", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig=="], + + "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], + + "web-tree-sitter": ["web-tree-sitter@0.24.5", "", {}, "sha512-+J/2VSHN8J47gQUAvF8KDadrfz6uFYVjxoxbKWDoXVsH2u7yLdarCnIURnrMA6uSRkgX3SdmqM5BOoQjPdSh5w=="], + + "web-vitals": ["web-vitals@5.3.0", "", {}, "sha512-q6LWsLatGYZp5VGBIOvbTj6JBV2nOmC8KvWztXBmwJcfFAzhwKwbOxhUH306XY3CcaZDUlSmSuNPBsCn0bFu+g=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "which-typed-array": ["which-typed-array@1.1.24", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.9", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-wk4Mf4pR5mRP7eYuuTBCIQ9d0ud2Fv2jRLQpfgnRjbOxAFHmjKFValgTpitVKzJJS8ajnYQV2Du1SZ8j6b/EUQ=="], + + "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], + + "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], + + "xml": ["xml@1.0.1", "", {}, "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw=="], + + "xml-but-prettier": ["xml-but-prettier@1.0.1", "", { "dependencies": { "repeat-string": "^1.5.2" } }, "sha512-C2CJaadHrZTqESlH03WOyw0oZTtoy2uEg6dSDF6YRg+9GnYNub53RRemLpnvtbHDFelxMx4LajiFsYeR6XJHgQ=="], + + "yaml": ["yaml@2.8.3", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg=="], + + "yoctocolors": ["yoctocolors@2.2.0", "", {}, "sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg=="], + + "yuku-analyzer": ["yuku-analyzer@0.10.2", "", { "dependencies": { "@yuku-toolchain/types": "^0.10.2", "yuku-ast": "^0.10.2" }, "optionalDependencies": { "@yuku-analyzer/binding-android-arm64": "0.10.2", "@yuku-analyzer/binding-darwin-arm64": "0.10.2", "@yuku-analyzer/binding-darwin-x64": "0.10.2", "@yuku-analyzer/binding-freebsd-x64": "0.10.2", "@yuku-analyzer/binding-linux-arm-gnu": "0.10.2", "@yuku-analyzer/binding-linux-arm-musl": "0.10.2", "@yuku-analyzer/binding-linux-arm64-gnu": "0.10.2", "@yuku-analyzer/binding-linux-arm64-musl": "0.10.2", "@yuku-analyzer/binding-linux-x64-gnu": "0.10.2", "@yuku-analyzer/binding-linux-x64-musl": "0.10.2", "@yuku-analyzer/binding-win32-arm64": "0.10.2", "@yuku-analyzer/binding-win32-x64": "0.10.2" } }, "sha512-UBRcDcZtIn14g88loZUZ123d6MOkc2hMTawUle5MkuWboowM90KbWHN3Zz+HfuS06sw935ygecBpcJlXHB/pOA=="], + + "yuku-ast": ["yuku-ast@0.10.2", "", { "dependencies": { "@yuku-toolchain/types": "^0.10.2" } }, "sha512-UnG9mA6giglCvSErft2/40TVFX750Sj1xgwddPLpG7J7rlr/P1wPADg9G2RK+d/1tNLtRVoLdMMOMVBB9561TQ=="], + + "zbsearch": ["zbsearch@4.0.1", "", {}, "sha512-lmnEEnb93QQcNv7qARDr7jBVPxYdZbZ9XdZ0dbZ0qaOvGne4jJMW4fjwPfVaUKQXv3FNWvGbDwRDgyaSb1pc8w=="], + + "zenscroll": ["zenscroll@4.0.2", "", {}, "sha512-jEA1znR7b4C/NnaycInCU6h/d15ZzCd1jmsruqOKnZP6WXQSMH3W2GL+OXbkruslU4h+Tzuos0HdswzRUk/Vgg=="], + + "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + + "@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], + + "@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], + + "@opentelemetry/otlp-transformer/@opentelemetry/resources": ["@opentelemetry/resources@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A=="], + + "@opentelemetry/sdk-logs/@opentelemetry/resources": ["@opentelemetry/resources@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A=="], + + "@opentelemetry/sdk-metrics/@opentelemetry/resources": ["@opentelemetry/resources@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A=="], + + "@opentelemetry/sdk-trace-base/@opentelemetry/resources": ["@opentelemetry/resources@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A=="], + + "@streamdown/code/shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="], + + "@swagger-api/apidom-parser-adapter-yaml-1-2/tree-sitter": ["tree-sitter@0.22.4", "", { "dependencies": { "node-addon-api": "^8.3.0", "node-gyp-build": "^4.8.4" } }, "sha512-usbHZP9/oxNsUY65MQUsduGRqDHQOou1cagUSwjhoSYAmSahjQDAVsh9s+SlZkn8X8+O1FULRGwHu7AFP3kjzg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.3", "tslib": "^2.4.0" }, "bundled": true }, "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.2.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" }, "bundled": true }, "sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.4", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "cytoscape-fcose/cose-base": ["cose-base@2.2.0", "", { "dependencies": { "layout-base": "^2.0.0" } }, "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g=="], + + "d3-dsv/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], + + "d3-sankey/d3-array": ["d3-array@2.12.1", "", { "dependencies": { "internmap": "^1.0.0" } }, "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ=="], + + "d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="], + + "fumadocs-core/yaml": ["yaml@2.9.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw=="], + + "fumadocs-mdx/magic-string": ["magic-string@1.4.2", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.6.0" } }, "sha512-vG+rjFRj1PqdIBozIxAGMjPlOhaVe+GXpbttY/iSK7rGcJRMlwNJO7dcUwmUqkymsFLJiNGI06t4D7Fr7yRC9g=="], + + "fumadocs-mdx/yaml": ["yaml@2.9.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw=="], + + "fumadocs-mdx/zod": ["zod@4.6.5", "", {}, "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q=="], + + "fumadocs-ui/lucide-react": ["lucide-react@1.51.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9929jimol5y7VbXUlVKcgURMSeIxFJB7dtWCkRAaNnF7KiCRkSkhmqWPUmAmUFYPezNawFk6qvz3Mu0gEnfI4g=="], + + "fumadocs-ui/motion": ["motion@13.5.1", "", { "dependencies": { "framer-motion": "13.5.1", "tslib": "^2.4.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-eV9SUem6KnzEa9+eT+dVBeDlhy5TTdn09YWR1WhUsKoZCJPw/n4TcYhjbxq2Gy5PTwDr//4H3r5vxkcZnNnO1w=="], + + "instantsearch-ui-components/@swc/helpers": ["@swc/helpers@0.5.18", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ=="], + + "instantsearch.js/@swc/helpers": ["@swc/helpers@0.5.18", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ=="], + + "katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], + + "log-update/slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="], + + "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + + "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + + "react-instantsearch/@swc/helpers": ["@swc/helpers@0.5.18", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ=="], + + "react-instantsearch-core/@swc/helpers": ["@swc/helpers@0.5.18", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ=="], + + "recharts/reselect": ["reselect@5.2.0", "", {}, "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw=="], + + "remarkable/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + + "streamdown/marked": ["marked@17.0.6", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA=="], + + "ultracite/zod": ["zod@4.6.5", "", {}, "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q=="], + + "vitest/vite": ["vite@8.3.2", "", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.7", "postcss": "^8.5.28", "rolldown": "~1.2.11", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.7.1", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-SQr1x6W5vVSbROg7vsyXIaxK9b0G7zsT68acdWWRmnBUsgDieLCRG+Rep9WdZgcposvv/GSnr4GUUBqB3vXq6w=="], + + "wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], + + "@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], + + "@streamdown/code/shiki/@shikijs/core": ["@shikijs/core@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA=="], + + "@streamdown/code/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA=="], + + "@streamdown/code/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g=="], + + "@streamdown/code/shiki/@shikijs/langs": ["@shikijs/langs@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg=="], + + "@streamdown/code/shiki/@shikijs/themes": ["@shikijs/themes@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA=="], + + "@streamdown/code/shiki/@shikijs/types": ["@shikijs/types@3.23.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ=="], + + "cytoscape-fcose/cose-base/layout-base": ["layout-base@2.0.1", "", {}, "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="], + + "d3-sankey/d3-array/internmap": ["internmap@1.0.1", "", {}, "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="], + + "d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="], + + "fumadocs-ui/motion/framer-motion": ["framer-motion@13.5.1", "", { "dependencies": { "motion-dom": "13.5.1", "motion-utils": "13.5.0", "tslib": "^2.4.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-HeWOaeHD3kyTSIEMT6+nw7fBn4saIfxxabZyAITbycAl4xr1kzGJKIvGt/OWghSlvIA9qTnZ1zTGelHAe6S8DA=="], + + "vitest/vite/lightningcss": ["lightningcss@1.33.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.33.0", "lightningcss-darwin-arm64": "1.33.0", "lightningcss-darwin-x64": "1.33.0", "lightningcss-freebsd-x64": "1.33.0", "lightningcss-linux-arm-gnueabihf": "1.33.0", "lightningcss-linux-arm64-gnu": "1.33.0", "lightningcss-linux-arm64-musl": "1.33.0", "lightningcss-linux-x64-gnu": "1.33.0", "lightningcss-linux-x64-musl": "1.33.0", "lightningcss-win32-arm64-msvc": "1.33.0", "lightningcss-win32-x64-msvc": "1.33.0" } }, "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA=="], + + "fumadocs-ui/motion/framer-motion/motion-dom": ["motion-dom@13.5.1", "", { "dependencies": { "motion-utils": "13.5.0" } }, "sha512-/7CVCD20Mz1KobktorpdgvowV0V41J7UxqE+qWS3GkSJqrRr5ft+2RloOYtEKv+wvBp3JaSFBclhxt4ja8ovHg=="], + + "fumadocs-ui/motion/framer-motion/motion-utils": ["motion-utils@13.5.0", "", {}, "sha512-Hqe/sqjIKliPZg8sVjWAkdySDPJNIeKH+euVkDOG8ri3DUUU7ZjndmHsGqEPz2iHpiyVaV5I22iguCMHU2he+Q=="], + + "vitest/vite/lightningcss/lightningcss-android-arm64": ["lightningcss-android-arm64@1.33.0", "", { "os": "android", "cpu": "arm64" }, "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg=="], + + "vitest/vite/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.33.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg=="], + + "vitest/vite/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.33.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ=="], + + "vitest/vite/lightningcss/lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.33.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg=="], + + "vitest/vite/lightningcss/lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.33.0", "", { "os": "linux", "cpu": "arm" }, "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ=="], + + "vitest/vite/lightningcss/lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg=="], + + "vitest/vite/lightningcss/lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ=="], + + "vitest/vite/lightningcss/lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg=="], + + "vitest/vite/lightningcss/lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw=="], + + "vitest/vite/lightningcss/lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.33.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA=="], + + "vitest/vite/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.33.0", "", { "os": "win32", "cpu": "x64" }, "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA=="], + } +} diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 000000000..1eaabb10c --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,6 @@ +[install] +# Only install packages published at least 3 days ago (supply-chain guard). +minimumReleaseAge = 259200 +# Arcade's own package is exempt: the design-system update workflow installs a +# release minutes after it is published. +minimumReleaseAgeExcludes = ["@arcadeai/design-system"] diff --git a/components.json b/components.json index a6993f9a7..3119458ad 100644 --- a/components.json +++ b/components.json @@ -4,18 +4,18 @@ "rsc": false, "tsx": true, "tailwind": { - "config": "tailwind.config.js", - "css": "styles/globals.css", + "config": "", + "css": "app/global.css", "baseColor": "neutral", "cssVariables": true, "prefix": "" }, "aliases": { - "components": "@/app/components", - "utils": "@/app/lib/utils", - "ui": "@/app/components/ui", - "lib": "@/app/lib", - "hooks": "@/app/hooks" + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" }, "iconLibrary": "lucide" } diff --git a/app/_components/agent-framework-tabs.tsx b/components/agent-framework-tabs.tsx similarity index 97% rename from app/_components/agent-framework-tabs.tsx rename to components/agent-framework-tabs.tsx index b1cbea94a..0c81a674f 100644 --- a/app/_components/agent-framework-tabs.tsx +++ b/components/agent-framework-tabs.tsx @@ -1,5 +1,5 @@ -import { Tabs } from "nextra/components"; -import { PlatformCard } from "@/app/_components/platform-card"; +import { Tabs } from "@/components/mdx/tabs"; +import { PlatformCard } from "@/components/platform-card"; export function AgentFrameworkTabs() { return ( diff --git a/app/_components/algolia-search.tsx b/components/algolia-search.tsx similarity index 100% rename from app/_components/algolia-search.tsx rename to components/algolia-search.tsx diff --git a/app/_components/analytics.tsx b/components/analytics.tsx similarity index 100% rename from app/_components/analytics.tsx rename to components/analytics.tsx diff --git a/app/_components/architecture-diagram.tsx b/components/architecture-diagram.tsx similarity index 100% rename from app/_components/architecture-diagram.tsx rename to components/architecture-diagram.tsx diff --git a/app/_components/back-button.tsx b/components/back-button.tsx similarity index 100% rename from app/_components/back-button.tsx rename to components/back-button.tsx diff --git a/app/_components/badges.tsx b/components/badges.tsx similarity index 100% rename from app/_components/badges.tsx rename to components/badges.tsx diff --git a/app/_components/button.tsx b/components/button.tsx similarity index 100% rename from app/_components/button.tsx rename to components/button.tsx diff --git a/app/_components/challenge-solution.tsx b/components/challenge-solution.tsx similarity index 100% rename from app/_components/challenge-solution.tsx rename to components/challenge-solution.tsx diff --git a/app/_components/cheat-sheet.tsx b/components/cheat-sheet.tsx similarity index 100% rename from app/_components/cheat-sheet.tsx rename to components/cheat-sheet.tsx diff --git a/components/chrome/collapse.tsx b/components/chrome/collapse.tsx new file mode 100644 index 000000000..363f2f7e6 --- /dev/null +++ b/components/chrome/collapse.tsx @@ -0,0 +1,85 @@ +"use client"; + +import { cn } from "@arcadeai/design-system/lib/utils"; +import { type ReactNode, useEffect, useRef, useState } from "react"; + +type CollapseProps = { + children: ReactNode; + className?: string; + closeDuration?: number; + /** Animate the width instead of the height (sidebar collapse). */ + horizontal?: boolean; + isOpen: boolean; + openDuration?: number; +}; + +/** + * Animated open/close container. The children stay in the DOM while closed + * (height 0, clipped) so every sidebar link is present in the served HTML. + */ +export function Collapse({ + children, + className, + closeDuration = 300, + horizontal = false, + isOpen, + openDuration = 500, +}: CollapseProps) { + const containerRef = useRef<HTMLDivElement>(null); + const [initialOpen] = useState(isOpen); + const animationRef = useRef<number>(0); + const previousOpen = useRef(isOpen); + + useEffect(() => { + // Animate only when the state really changed, not on mount or re-mount. + if (previousOpen.current === isOpen) { + return; + } + previousOpen.current = isOpen; + const container = containerRef.current; + if (animationRef.current) { + clearTimeout(animationRef.current); + animationRef.current = 0; + } + const child = container?.firstElementChild; + if (!(container && child instanceof HTMLElement)) { + return; + } + if (horizontal) { + child.style.width = `${child.clientWidth}px`; + container.style.width = `${child.clientWidth}px`; + } else { + container.style.height = `${child.clientHeight}px`; + } + if (isOpen) { + animationRef.current = window.setTimeout(() => { + container.style.removeProperty("height"); + }, openDuration); + } else { + requestAnimationFrame(() => { + if (horizontal) { + container.style.width = "0"; + } else { + container.style.height = "0"; + } + }); + } + }, [horizontal, isOpen, openDuration]); + + return ( + <div + className={cn( + "transform-gpu transition-all ease-in-out motion-reduce:transition-none", + isOpen ? "opacity-100" : "overflow-hidden opacity-0", + className + )} + ref={containerRef} + style={{ + ...(initialOpen || horizontal ? undefined : { height: 0 }), + transitionDuration: `${isOpen ? openDuration : closeDuration}ms`, + }} + > + {children} + </div> + ); +} diff --git a/components/chrome/docs-layout.tsx b/components/chrome/docs-layout.tsx new file mode 100644 index 000000000..612fb8851 --- /dev/null +++ b/components/chrome/docs-layout.tsx @@ -0,0 +1,107 @@ +"use client"; + +import type * as PageTree from "fumadocs-core/page-tree"; +import { + SidebarCollapseTrigger, + SidebarProvider, + SidebarTrigger, + useSidebar, +} from "fumadocs-ui/components/sidebar/base"; +import { DocsLayout, type DocsSlots } from "fumadocs-ui/layouts/notebook"; +import { usePathname } from "next/navigation"; +import { type ComponentProps, createContext, type ReactNode, use } from "react"; +import { Navbar } from "@/components/chrome/navbar"; +import { MobileNav, Sidebar } from "@/components/chrome/sidebar"; +import type { SidebarNode } from "@/components/chrome/sidebar-model"; +import { DirectoriesContext } from "@/components/chrome/stores"; + +/** True on pages that show no sidebar (the 404 page). */ +const BareContext = createContext(false); + +/** + * Page frame: skip link, navbar, mobile menu, then the centered row that + * holds the sidebar, the page and its table of contents. + */ +function Container({ children }: ComponentProps<"div">) { + const directories = use(DirectoriesContext); + const bare = use(BareContext); + return ( + <> + <a className="nextra-skip-nav" href="#nextra-skip-nav"> + Skip to Content + </a> + <Navbar /> + <MobileNav directories={directories} /> + <main className="custom-main"> + {bare ? ( + children + ) : ( + <div className="mx-auto flex max-w-(--nextra-content-width)"> + {children} + </div> + )} + </main> + </> + ); +} + +/** + * The sidebar starts fresh on every page, as it always has: the active entry + * is centered again, a collapsed sidebar re-expands, and folders the reader + * opened by hand return to their default state one navigation later. + */ +function SidebarRoot() { + const directories = use(DirectoriesContext); + const pathname = usePathname(); + return use(BareContext) ? null : ( + <Sidebar directories={directories} key={pathname} /> + ); +} + +const slots: Partial<DocsSlots> = { + container: Container, + sidebar: { + collapseTrigger: SidebarCollapseTrigger, + provider: SidebarProvider, + root: SidebarRoot, + trigger: SidebarTrigger, + useSidebar, + }, +}; + +type ChromeLayoutProps = { + /** Render the page without the sidebar and the centered content row. */ + bare?: boolean; + children: ReactNode; + /** Sidebar entries, see components/chrome/sidebar-model.ts. */ + directories: SidebarNode[]; + tree: PageTree.Root; +}; + +/** + * Docs layout. Fumadocs provides the page tree, sidebar state and search + * contexts; every visible part is rendered by the components in this folder. + */ +export function ChromeLayout({ + bare = false, + children, + directories, + tree, +}: ChromeLayoutProps) { + return ( + <DirectoriesContext value={directories}> + <BareContext value={bare}> + <DocsLayout + nav={{ enabled: false }} + searchToggle={{ enabled: false }} + slots={slots} + tabs={false} + themeSwitch={{ enabled: false }} + tree={tree} + > + {children} + </DocsLayout> + </BareContext> + </DirectoriesContext> + ); +} diff --git a/components/chrome/icons.tsx b/components/chrome/icons.tsx new file mode 100644 index 000000000..318fc2492 --- /dev/null +++ b/components/chrome/icons.tsx @@ -0,0 +1,177 @@ +import type { SVGProps } from "react"; + +/** + * Icons used by the docs chrome. The paths are the ones the previous docs theme + * shipped, so every glyph keeps its exact shape. + */ + +type IconProps = SVGProps<SVGSVGElement>; + +export function ArrowRightIcon(props: IconProps) { + return ( + <svg + aria-hidden="true" + fill="none" + stroke="currentColor" + strokeWidth={2} + viewBox="0 0 24 24" + {...props} + > + <path d="M9 5l7 7-7 7" strokeLinecap="round" strokeLinejoin="round" /> + </svg> + ); +} + +export function ExpandIcon(props: IconProps) { + return ( + <svg aria-hidden="true" fill="currentColor" viewBox="0 0 16 16" {...props}> + <path d="M4.177 7.823l2.396-2.396A.25.25 0 017 5.604v4.792a.25.25 0 01-.427.177L4.177 8.177a.25.25 0 010-.354z" /> + <path + d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0114.25 16H1.75A1.75 1.75 0 010 14.25V1.75zm1.75-.25a.25.25 0 00-.25.25v12.5c0 .138.112.25.25.25H9.5v-13H1.75zm12.5 13H11v-13h3.25a.25.25 0 01.25.25v12.5a.25.25 0 01-.25.25z" + fillRule="evenodd" + /> + </svg> + ); +} + +export function GlobeIcon(props: IconProps) { + return ( + <svg aria-hidden="true" fill="currentColor" viewBox="2 2 16 16" {...props}> + <path + d="M4.083 9h1.946c.089-1.546.383-2.97.837-4.118A6.004 6.004 0 004.083 9zM10 2a8 8 0 100 16 8 8 0 000-16zm0 2c-.076 0-.232.032-.465.262-.238.234-.497.623-.737 1.182-.389.907-.673 2.142-.766 3.556h3.936c-.093-1.414-.377-2.649-.766-3.556-.24-.56-.5-.948-.737-1.182C10.232 4.032 10.076 4 10 4zm3.971 5c-.089-1.546-.383-2.97-.837-4.118A6.004 6.004 0 0115.917 9h-1.946zm-2.003 2H8.032c.093 1.414.377 2.649.766 3.556.24.56.5.948.737 1.182.233.23.389.262.465.262.076 0 .232-.032.465-.262.238-.234.498-.623.737-1.182.389-.907.673-2.142.766-3.556zm1.166 4.118c.454-1.147.748-2.572.837-4.118h1.946a6.004 6.004 0 01-2.783 4.118zm-6.268 0C6.412 13.97 6.118 12.546 6.03 11H4.083a6.004 6.004 0 002.783 4.118z" + fillRule="evenodd" + /> + </svg> + ); +} + +export function MoonIcon(props: IconProps) { + return ( + <svg + aria-hidden="true" + fill="currentColor" + stroke="currentColor" + viewBox="2 2 20 20" + {...props} + > + <path + d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" + strokeLinejoin="round" + strokeWidth={2} + /> + </svg> + ); +} + +export function SunIcon(props: IconProps) { + return ( + <svg + aria-hidden="true" + fill="currentColor" + stroke="currentColor" + viewBox="2 2 20 20" + {...props} + > + <path + d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" + strokeLinecap="round" + strokeWidth={2} + /> + </svg> + ); +} + +export function MenuIcon(props: IconProps) { + return ( + <svg + aria-hidden="true" + stroke="currentColor" + strokeLinecap="round" + strokeWidth={2} + viewBox="0 0 24 24" + {...props} + > + <g> + <path d="M4 6h16" /> + </g> + <path d="M4 12h16" /> + <g> + <path d="M4 18h16" /> + </g> + </svg> + ); +} + +export function LinkArrowIcon(props: IconProps) { + return ( + <svg + aria-hidden="true" + fill="none" + stroke="currentColor" + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth={1.7} + viewBox="0 0 24 24" + {...props} + > + <path d="M7 17L17 7" /> + <path d="M7 7h10v10" /> + </svg> + ); +} + +export function CopyIcon(props: IconProps) { + return ( + <svg + aria-hidden="true" + fill="none" + stroke="currentColor" + strokeWidth={2} + viewBox="0 0 24 24" + {...props} + > + <rect height={13} rx={2} width={13} x={9} y={9} /> + <path d="M5 15H4C2.89543 15 2 14.1046 2 13V4C2 2.89543 2.89543 2 4 2H13C14.1046 2 15 2.89543 15 4V5" /> + </svg> + ); +} + +export function ChatGPTIcon(props: IconProps) { + return ( + <svg aria-hidden="true" fill="currentColor" viewBox="0 0 20 20" {...props}> + <path d="M11.2475 18.25C10.6975 18.25 10.175 18.1455 9.67999 17.9365C9.18499 17.7275 8.74499 17.436 8.35999 17.062C7.94199 17.205 7.50749 17.2765 7.05649 17.2765C6.31949 17.2765 5.63749 17.095 5.01049 16.732C4.38349 16.369 3.87749 15.874 3.49249 15.247C3.11849 14.62 2.93149 13.9215 2.93149 13.1515C2.93149 12.8325 2.97549 12.486 3.06349 12.112C2.62349 11.705 2.28249 11.2375 2.04049 10.7095C1.79849 10.1705 1.67749 9.6095 1.67749 9.0265C1.67749 8.4325 1.80399 7.8605 2.05699 7.3105C2.30999 6.7605 2.66199 6.2875 3.11299 5.8915C3.57499 5.4845 4.10849 5.204 4.71349 5.05C4.83449 4.423 5.08749 3.862 5.47249 3.367C5.86849 2.861 6.35249 2.465 6.92449 2.179C7.49649 1.893 8.10699 1.75 8.75599 1.75C9.30599 1.75 9.82849 1.8545 10.3235 2.0635C10.8185 2.2725 11.2585 2.564 11.6435 2.938C12.0615 2.795 12.496 2.7235 12.947 2.7235C13.684 2.7235 14.366 2.905 14.993 3.268C15.62 3.631 16.1205 4.126 16.4945 4.753C16.8795 5.38 17.072 6.0785 17.072 6.8485C17.072 7.1675 17.028 7.514 16.94 7.888C17.38 8.295 17.721 8.768 17.963 9.307C18.205 9.835 18.326 10.3905 18.326 10.9735C18.326 11.5675 18.1995 12.1395 17.9465 12.6895C17.6935 13.2395 17.336 13.718 16.874 14.125C16.423 14.521 15.895 14.796 15.29 14.95C15.169 15.577 14.9105 16.138 14.5145 16.633C14.1295 17.139 13.651 17.535 13.079 17.821C12.507 18.107 11.8965 18.25 11.2475 18.25ZM7.17199 16.1875C7.72199 16.1875 8.20049 16.072 8.60749 15.841L11.7095 14.059C11.8195 13.982 11.8745 13.8775 11.8745 13.7455V12.3265L7.88149 14.62C7.63949 14.763 7.39749 14.763 7.15549 14.62L4.03699 12.8215C4.03699 12.8545 4.03149 12.893 4.02049 12.937C4.02049 12.981 4.02049 13.047 4.02049 13.135C4.02049 13.696 4.15249 14.213 4.41649 14.686C4.69149 15.148 5.07099 15.511 5.55499 15.775C6.03899 16.05 6.57799 16.1875 7.17199 16.1875ZM7.33699 13.498C7.40299 13.531 7.46349 13.5475 7.51849 13.5475C7.57349 13.5475 7.62849 13.531 7.68349 13.498L8.92099 12.7885L4.94449 10.4785C4.70249 10.3355 4.58149 10.121 4.58149 9.835V6.2545C4.03149 6.4965 3.59149 6.8705 3.26149 7.3765C2.93149 7.8715 2.76649 8.4215 2.76649 9.0265C2.76649 9.5655 2.90399 10.0825 3.17899 10.5775C3.45399 11.0725 3.81149 11.4465 4.25149 11.6995L7.33699 13.498ZM11.2475 17.161C11.8305 17.161 12.3585 17.029 12.8315 16.765C13.3045 16.501 13.6785 16.138 13.9535 15.676C14.2285 15.214 14.366 14.697 14.366 14.125V10.561C14.366 10.429 14.311 10.33 14.201 10.264L12.947 9.538V14.1415C12.947 14.4275 12.826 14.642 12.584 14.785L9.46549 16.5835C10.0045 16.9685 10.5985 17.161 11.2475 17.161ZM11.8745 11.122V8.878L10.01 7.822L8.12899 8.878V11.122L10.01 12.178L11.8745 11.122ZM7.05649 5.8585C7.05649 5.5725 7.17749 5.358 7.41949 5.215L10.538 3.4165C9.99899 3.0315 9.40499 2.839 8.75599 2.839C8.17299 2.839 7.64499 2.971 7.17199 3.235C6.69899 3.499 6.32499 3.862 6.04999 4.324C5.78599 4.786 5.65399 5.303 5.65399 5.875V9.4225C5.65399 9.5545 5.70899 9.659 5.81899 9.736L7.05649 10.462V5.8585ZM15.4385 13.7455C15.9885 13.5035 16.423 13.1295 16.742 12.6235C17.072 12.1175 17.237 11.5675 17.237 10.9735C17.237 10.4345 17.0995 9.9175 16.8245 9.4225C16.5495 8.9275 16.192 8.5535 15.752 8.3005L12.6665 6.5185C12.6005 6.4745 12.54 6.458 12.485 6.469C12.43 6.469 12.375 6.4855 12.32 6.5185L11.0825 7.2115L15.0755 9.538C15.1965 9.604 15.2845 9.692 15.3395 9.802C15.4055 9.901 15.4385 10.022 15.4385 10.165V13.7455ZM12.122 5.3635C12.364 5.2095 12.606 5.2095 12.848 5.3635L15.983 7.195C15.983 7.118 15.983 7.019 15.983 6.898C15.983 6.37 15.851 5.8695 15.587 5.3965C15.334 4.9125 14.9655 4.5275 14.4815 4.2415C14.0085 3.9555 13.4585 3.8125 12.8315 3.8125C12.2815 3.8125 11.803 3.928 11.396 4.159L8.29399 5.941C8.18399 6.018 8.12899 6.1225 8.12899 6.2545V7.6735L12.122 5.3635Z" /> + </svg> + ); +} + +export function ClaudeIcon(props: IconProps) { + return ( + <svg + aria-hidden="true" + fill="currentColor" + viewBox="0 0 100 101" + {...props} + > + <path d="M96.0000 40.0000 L99.5002 42.0000 L99.5002 43.5000 L98.5000 47.0000 L56.0000 57.0000 L52.0040 47.0708 L96.0000 40.0000 M96.0000 40.0000 " /> + <path d="M80.1032 10.5903 L84.9968 11.6171 L86.2958 13.2179 L87.5346 17.0540 L87.0213 19.5007 L58.5000 58.5000 L49.0000 49.0000 L75.3008 14.4873 L80.1032 10.5903 M80.1032 10.5903 " /> + <path d="M55.5002 4.5000 L58.5005 2.5000 L61.0002 3.5000 L63.5002 7.0000 L56.6511 48.1620 L52.0005 45.0000 L50.0005 39.5000 L53.5003 8.5000 L55.5002 4.5000 M55.5002 4.5000 " /> + <path d="M23.4253 5.1588 L26.5075 1.2217 L28.5175 0.7632 L32.5063 1.3458 L34.4748 2.8868 L48.8202 34.6902 L54.0089 49.8008 L47.9378 53.1760 L24.8009 11.1886 L23.4253 5.1588 M23.4253 5.1588 " /> + <path d="M8.4990 27.0019 L7.4999 23.0001 L10.5003 19.5001 L14.0003 20.0001 L15.0003 20.0001 L36.0000 35.5000 L42.5000 40.5000 L51.5000 47.5000 L46.5000 56.0000 L42.0002 52.5000 L39.0001 49.5000 L10.0000 29.0001 L8.4990 27.0019 M8.4990 27.0019 " /> + <path d="M2.5003 53.0000 L0.2370 50.5000 L0.2373 48.2759 L2.5003 47.5000 L28.0000 49.0000 L53.0000 51.0000 L52.1885 55.9782 L4.5000 53.5000 L2.5003 53.0000 M2.5003 53.0000 " /> + <path d="M17.5002 79.0264 L12.5005 79.0264 L10.5124 76.7369 L10.5124 74.0000 L19.0005 68.0000 L53.5082 46.0337 L57.0005 52.0000 L17.5002 79.0264 M17.5002 79.0264 " /> + <path d="M27.0004 92.9999 L25.0003 93.4999 L22.0003 91.9999 L22.5004 89.4999 L52.0003 50.5000 L56.0004 55.9999 L34.0003 85.0000 L27.0004 92.9999 M27.0004 92.9999 " /> + <path d="M51.9998 98.0000 L50.5002 100.0000 L47.5002 101.0000 L45.0001 99.0000 L43.5000 96.0000 L51.0003 55.4999 L55.5001 55.9999 L51.9998 98.0000 M51.9998 98.0000 " /> + <path d="M77.5007 86.9997 L77.5007 90.9997 L77.0006 92.4997 L75.0004 93.4997 L71.5006 93.0339 L47.4669 57.2642 L56.9998 50.0002 L64.9994 64.5004 L65.7507 69.7497 L77.5007 86.9997 M77.5007 86.9997 " /> + <path d="M89.0008 80.9991 L89.5008 83.4991 L88.0008 85.4991 L86.5007 84.9991 L78.0007 78.9991 L65.0007 67.4991 L55.0007 60.4991 L58.0000 51.0000 L62.9999 54.0001 L66.0007 59.4991 L89.0008 80.9991 M89.0008 80.9991 " /> + <path d="M82.5003 55.5000 L95.0003 56.5000 L98.0003 58.5000 L100.0000 61.5000 L100.0000 63.6587 L94.5003 66.0000 L66.5005 59.0000 L55.0003 58.5000 L58.0000 48.0000 L66.0005 54.0000 L82.5003 55.5000 M82.5003 55.5000 " /> + </svg> + ); +} + +export function CheckIcon(props: IconProps) { + return ( + <svg aria-hidden="true" fill="currentColor" viewBox="0 0 20 20" {...props}> + <path d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" /> + </svg> + ); +} diff --git a/components/chrome/navbar.tsx b/components/chrome/navbar.tsx new file mode 100644 index 000000000..4d0c55be2 --- /dev/null +++ b/components/chrome/navbar.tsx @@ -0,0 +1,85 @@ +"use client"; + +import { Discord, Github } from "@arcadeai/design-system"; +import { cn } from "@arcadeai/design-system/lib/utils"; +import { useSidebar } from "fumadocs-ui/components/sidebar/base"; +import Link from "next/link"; +import { useCallback } from "react"; +import { AlgoliaSearch } from "@/components/algolia-search"; +import { MenuIcon } from "@/components/chrome/icons"; +import { Logo } from "@/components/logo"; +import { NavLinks } from "@/components/nav-links"; +import { DISCORD_URL, GITHUB_REPO_URL, LOGO_URL } from "@/lib/layout.shared"; + +/** + * Top navigation bar: logo (links to arcade.dev), search, GitHub and Discord + * icons, the Sign Up / Dashboard buttons and, on small screens, the menu + * button that opens the mobile navigation. + */ +export function Navbar() { + const { open, setOpen } = useSidebar(); + const toggleMenu = useCallback(() => setOpen((prev) => !prev), [setOpen]); + + return ( + <header + className={cn( + "nextra-navbar sticky top-0 z-30 w-full bg-transparent print:hidden", + "max-md:[.nextra-banner:not([class$=hidden])~&]:top-(--nextra-banner-height)" + )} + > + <div + className={cn( + "nextra-navbar-blur", + "absolute -z-1 size-full", + "nextra-border border-b", + "bg-nextra-bg/70 backdrop-blur-md" + )} + /> + <nav + className={cn( + "mx-auto flex max-w-(--nextra-content-width) items-center gap-4", + "pr-[max(env(safe-area-inset-right),1.5rem)] pl-[max(env(safe-area-inset-left),1.5rem)]", + "justify-end" + )} + style={{ height: "var(--nextra-navbar-height)" }} + > + <Link + aria-label="Home page" + className="me-auto flex items-center transition-opacity hover:opacity-75 focus-visible:nextra-focus" + href={LOGO_URL} + > + <Logo /> + </Link> + <div className="nextra-scrollbar flex gap-4 overflow-x-auto py-1.5 max-md:hidden" /> + <div className="max-md:hidden"> + <AlgoliaSearch /> + </div> + <a + className="focus-visible:nextra-focus" + href={GITHUB_REPO_URL} + rel="noreferrer" + target="_blank" + > + <Github className="size-5.5 transition-colors duration-150 ease-in-out [&>path]:fill-current" /> + </a> + <a + className="focus-visible:nextra-focus" + href={DISCORD_URL} + rel="noreferrer" + target="_blank" + > + <Discord className="size-6 transition-colors duration-150 ease-in-out [&>path]:fill-current" /> + </a> + <NavLinks /> + <button + aria-label="Menu" + className="nextra-hamburger cursor-pointer transition focus-visible:nextra-focus active:bg-gray-400/20 md:hidden" + onClick={toggleMenu} + type="button" + > + <MenuIcon className={cn({ open })} height="24" /> + </button> + </nav> + </header> + ); +} diff --git a/components/chrome/not-found-content.tsx b/components/chrome/not-found-content.tsx new file mode 100644 index 000000000..5ee860590 --- /dev/null +++ b/components/chrome/not-found-content.tsx @@ -0,0 +1,171 @@ +"use client"; + +import { + Button, + buttonVariants, + Card, + CardContent, + CardFooter, + CardHeader, +} from "@arcadeai/design-system"; +import { cn } from "@arcadeai/design-system/lib/utils"; +import { Home, SearchX } from "lucide-react"; +import Link from "next/link"; +import { usePathname, useSearchParams } from "next/navigation"; +import posthog from "posthog-js"; +import { useEffect, useMemo, useRef, useState } from "react"; +import { BackButton } from "@/components/back-button"; +import type { Dictionary } from "@/lib/dictionaries/i18n-config"; + +const LOCALE_PATH_REGEX = /^\/([a-z]{2}(?:-[A-Z]{2})?)(?:\/.+|$)/; +function getReferrerInfo(): { referrer: string; referringDomain: string } { + const referrer = + typeof document === "undefined" ? "" : document.referrer || ""; + if (!referrer) { + return { referrer: "", referringDomain: "" }; + } + try { + return { referrer, referringDomain: new URL(referrer).hostname }; + } catch { + return { referrer, referringDomain: "" }; + } +} + +// Default dictionary for fallback during SSR +const defaultDict: Dictionary["notFoundPage"] = { + createMcpServer: "Create a MCP Server", + description: "This page doesn't exist or may have moved.", + goBack: "Go back", + goHome: "Go to homepage", + mcpServers: "MCP Servers", + needHelp: "Need help? Try these popular pages:", + notAvailablePrefix: "Not available in", + quickstart: "Quickstart", + title: "Page not found", + translationHint: "This page may not be translated yet.", + tryEnglish: "Try English version", + viewOriginalEnglish: "View the original in English", +}; + +/** The 404 card. Reports the missing URL to analytics once per URL. */ +export function NotFoundContent() { + const pathname = usePathname() || "/"; + const searchParams = useSearchParams(); + const lastCapturedPathRef = useRef<string | null>(null); + const [dict, setDict] = useState<Dictionary["notFoundPage"]>(defaultDict); + + const currentLocale = useMemo( + () => pathname.match(LOCALE_PATH_REGEX)?.[1] || "en", + [pathname] + ); + + useEffect(() => { + const loadDict = async () => { + try { + const dictModule = await import("@/lib/dictionaries/en"); + setDict(dictModule.default.notFoundPage); + } catch { + // Keep default English dictionary on error + } + }; + loadDict(); + }, []); + + useEffect(() => { + const search = searchParams.toString(); + const pathWithQuery = search ? `${pathname}?${search}` : pathname; + + // Prevent duplicate capture if this component re-renders for the same 404 URL. + if (lastCapturedPathRef.current === pathWithQuery) { + return; + } + lastCapturedPathRef.current = pathWithQuery; + + const { referrer, referringDomain } = getReferrerInfo(); + + posthog.capture("Page not found", { + // Ensure special props are attached to custom 404 events for link analysis. + $current_url: typeof window === "undefined" ? null : window.location.href, + $referrer: referrer || null, + $referring_domain: referringDomain || null, + locale: currentLocale, + path: pathWithQuery, + pathname, + status_code: 404, + }); + }, [currentLocale, pathname, searchParams]); + + return ( + <div className="flex items-center justify-center py-16"> + <div className="w-full max-w-xl"> + <Card className="gap-4 border-2 border-stone-900 py-6 shadow-2xl shadow-black/10 dark:shadow-black/25"> + <CardHeader className="text-center"> + {/* Illustration */} + <div className="mx-auto flex size-16 items-center justify-center rounded-full bg-linear-to-br from-orange-100 to-orange-50 ring-1 ring-orange-200/50 dark:from-orange-900/30 dark:to-orange-800/20 dark:ring-orange-800/30"> + <SearchX className="size-9 text-orange-600 dark:text-orange-400" /> + </div> + </CardHeader> + + <CardContent className="space-y-6 px-10"> + {/* Content */} + <div className="space-y-3 text-center"> + <h1 className="font-bold text-2xl text-foreground tracking-tight sm:text-3xl"> + {dict.title} + </h1> + <p className="mx-auto max-w-sm text-base text-muted-foreground leading-relaxed"> + {dict.description} + </p> + </div> + + {/* Actions */} + <div className="flex flex-col items-center justify-center gap-3 sm:flex-row"> + <Button + render={<Link href={`/${currentLocale}/home`} />} + size="lg" + > + <Home className="mr-2 h-5 w-5" /> + {dict.goHome} + </Button> + <BackButton goBackText={dict.goBack} /> + </div> + </CardContent> + + <CardFooter className="border-border border-t pt-6"> + <div className="w-full space-y-3 text-center"> + <p className="text-muted-foreground text-sm">{dict.needHelp}</p> + <div className="flex flex-wrap items-center justify-center gap-2"> + <Link + className={cn( + buttonVariants({ size: "sm", variant: "ghost" }), + "h-8" + )} + href={`/${currentLocale}/get-started/quickstarts/call-tool-agent`} + > + {dict.quickstart} + </Link> + <Link + className={cn( + buttonVariants({ size: "sm", variant: "ghost" }), + "h-8" + )} + href={`/${currentLocale}/mcp-servers`} + > + {dict.mcpServers} + </Link> + <Link + className={cn( + buttonVariants({ size: "sm", variant: "ghost" }), + "h-8" + )} + href={`/${currentLocale}/build/create-tools/tool-basics/build-mcp-server`} + > + {dict.createMcpServer} + </Link> + </div> + </div> + </CardFooter> + </Card> + </div> + </div> + ); +} diff --git a/components/chrome/page.tsx b/components/chrome/page.tsx new file mode 100644 index 000000000..81b71e7c9 --- /dev/null +++ b/components/chrome/page.tsx @@ -0,0 +1,487 @@ +"use client"; + +import { cn } from "@arcadeai/design-system/lib/utils"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { + Fragment, + type MouseEvent, + type ReactNode, + use, + useEffect, + useRef, +} from "react"; +import { ArrowRightIcon, LinkArrowIcon } from "@/components/chrome/icons"; +import type { + SidebarFolder, + SidebarNode, + SidebarPage, +} from "@/components/chrome/sidebar-model"; +import { + DirectoriesContext, + setActiveAnchor, + type TocEntry, + useActiveAnchor, +} from "@/components/chrome/stores"; +import { PageActions } from "@/components/page-actions"; + +/* ------------------------------------------------------------------ helpers */ + +function isActive(route: string, href: string): boolean { + return [route, `${route}/`].includes(`${href}/`); +} + +function nodeRoute(node: SidebarNode | undefined): string { + if (!node) { + return ""; + } + if (node.kind === "page") { + return node.href; + } + return node.kind === "folder" ? node.route : ""; +} + +type ActiveInfo = { + /** The pages before and after the current one in the previous/next sequence. */ + next?: SidebarPage; + /** Sidebar entries from the top-level section down to the current page. */ + path: (SidebarFolder | SidebarPage)[]; + prev?: SidebarPage; +}; + +/** + * Walks the sidebar in display order. Link-only entries (toolkit pages, cross + * links) are not part of the previous/next sequence; when one of them is the + * current page, its neighbours are the real pages around it. + */ +function findActive(directories: SidebarNode[], route: string): ActiveInfo { + const flat: SidebarPage[] = []; + let index = -1; + let inSequence = false; + let path: (SidebarFolder | SidebarPage)[] = []; + + const visitPage = (node: SidebarPage, parents: SidebarFolder[]) => { + if (index === -1 && isActive(route, node.href)) { + index = flat.length; + inSequence = !node.linkOnly; + path = [...parents, node]; + } + if (!node.linkOnly) { + flat.push(node); + } + }; + const walk = (nodes: SidebarNode[], parents: SidebarFolder[]) => { + for (const node of nodes) { + if (node.kind === "folder") { + walk(node.children, [...parents, node]); + } else if (node.kind === "page") { + visitPage(node, parents); + } + } + }; + walk(directories, []); + if (index === -1) { + return { path }; + } + return { + next: flat[inSequence ? index + 1 : index], + path, + prev: flat[index - 1], + }; +} + +function Title({ node }: { node: SidebarFolder | SidebarPage }) { + if (!node.icon) { + return node.title; + } + return ( + <span className="flex items-center gap-2 font-medium text-gray-950 dark:text-white [&>svg]:size-4"> + {node.icon} + {node.title} + </span> + ); +} + +function titleAttribute(node: SidebarFolder | SidebarPage): string | undefined { + return typeof node.title === "string" ? node.title : undefined; +} + +/* --------------------------------------------------------------- breadcrumb */ + +function Breadcrumb({ path }: { path: (SidebarFolder | SidebarPage)[] }) { + return ( + <div className="nextra-breadcrumb mt-1.5 flex items-center gap-1 overflow-hidden text-gray-600 text-sm contrast-more:text-current dark:text-gray-400"> + {path.map((item, index) => { + const nextItem = path[index + 1]; + let href = ""; + if (nextItem && item.kind === "folder") { + const first = nodeRoute(item.children[0]); + href = first === nodeRoute(nextItem) ? "" : first; + } + const className = cn( + "whitespace-nowrap transition-colors", + nextItem + ? "min-w-6 overflow-hidden text-ellipsis" + : "font-medium text-black dark:text-gray-100", + href && + "ring-inset hover:text-gray-900 focus-visible:nextra-focus dark:hover:text-gray-100" + ); + return ( + <Fragment key={item.id}> + {index > 0 ? ( + <ArrowRightIcon className="shrink-0 rtl:rotate-180" height="14" /> + ) : null} + {href ? ( + <Link + className={className} + href={href} + prefetch={false} + title={titleAttribute(item)} + > + <Title node={item} /> + </Link> + ) : ( + <span className={className} title={titleAttribute(item)}> + <Title node={item} /> + </span> + )} + </Fragment> + ); + })} + </div> + ); +} + +/* --------------------------------------------------------------- pagination */ + +const paginationLink = cn( + "text-gray-600 focus-visible:nextra-focus dark:text-gray-400", + "hover:text-gray-800 dark:hover:text-gray-200", + "contrast-more:text-gray-700 contrast-more:dark:text-gray-100", + "flex max-w-[50%] items-center gap-1 py-4 font-medium text-base transition-colors [word-break:break-word] md:text-lg" +); + +function Pagination({ + next, + prev, +}: { + next?: SidebarPage; + prev?: SidebarPage; +}) { + if (!(prev || next)) { + return null; + } + return ( + <div className="nextra-border mb-8 flex items-center border-t pt-8 print:hidden"> + {prev ? ( + <Link + className={cn(paginationLink, "pe-4")} + href={prev.href} + prefetch={false} + title={titleAttribute(prev)} + > + <ArrowRightIcon + className="inline shrink-0 ltr:rotate-180" + height="20" + /> + <Title node={prev} /> + </Link> + ) : null} + {next ? ( + <Link + className={cn(paginationLink, "ms-auto ps-4 text-end")} + href={next.href} + prefetch={false} + title={titleAttribute(next)} + > + <Title node={next} /> + <ArrowRightIcon + className="inline shrink-0 rtl:rotate-180" + height="20" + /> + </Link> + ) : null} + </div> + ); +} + +/* -------------------------------------------------------- table of contents */ + +const DEPTH_CLASS: Record<number, string> = { + 2: "font-semibold", + 3: "ms-3", + 4: "ms-6", + 5: "ms-9", + 6: "ms-12", +}; + +const tocLink = cn( + "font-medium text-xs transition", + "text-gray-600 dark:text-gray-400", + "hover:text-gray-800 dark:hover:text-gray-200", + "contrast-more:text-gray-700 contrast-more:dark:text-gray-100" +); + +const SCROLL_TO_OPTIONS = { behavior: "smooth", top: 0 } as const; + +function scrollToTop(event: MouseEvent<HTMLButtonElement>) { + const buttonElement = event.currentTarget; + const tocElement = buttonElement.parentElement?.parentElement; + window.scrollTo(SCROLL_TO_OPTIONS); + tocElement?.scrollTo(SCROLL_TO_OPTIONS); + buttonElement.disabled = true; +} + +/** Keeps the active entry visible inside a scrollable table of contents. */ +function scrollEntryIntoView(list: HTMLElement, anchor: HTMLElement) { + const listRect = list.getBoundingClientRect(); + const anchorRect = anchor.getBoundingClientRect(); + if (anchorRect.top >= listRect.top && anchorRect.bottom <= listRect.bottom) { + return; + } + list.scrollTo({ + behavior: "smooth", + top: + list.scrollTop + + anchorRect.top - + listRect.top - + (list.clientHeight - anchorRect.height) / 2, + }); +} + +function TOC({ editUrl, toc }: { editUrl?: string; toc: TocEntry[] }) { + const activeSlug = useActiveAnchor(); + const tocRef = useRef<HTMLUListElement>(null); + const hasHeadings = toc.length > 0; + const activeIndex = toc.findIndex(({ id }) => id === activeSlug); + + useEffect(() => { + if (!activeSlug) { + return; + } + const list = tocRef.current; + const anchor = list?.querySelector<HTMLElement>( + `a[href="#${CSS.escape(activeSlug)}"]` + ); + if (list && anchor) { + scrollEntryIntoView(list, anchor); + } + }, [activeSlug]); + + return ( + <div + className={cn( + "grid grid-rows-[min-content_1fr_min-content]", + "sticky top-(--nextra-navbar-height) text-sm", + "max-h-[calc(100vh-var(--nextra-navbar-height))]" + )} + > + {hasHeadings ? ( + <> + <p className="px-4 pt-6 font-semibold tracking-tight">On this page</p> + <ul + className={cn( + "nextra-scrollbar overflow-y-auto overscroll-y-contain hyphens-auto p-4", + "nextra-mask" + )} + ref={tocRef} + > + {toc.map(({ id, title, depth }) => ( + <li className="my-2 scroll-my-6 scroll-py-6" key={id}> + <a + className={cn( + "focus-visible:nextra-focus", + DEPTH_CLASS[depth], + "block subpixel-antialiased transition-colors", + id === activeSlug + ? "text-primary-600 contrast-more:text-primary-600!" + : "text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-300", + "break-words contrast-more:text-gray-900 contrast-more:underline contrast-more:dark:text-gray-50" + )} + href={`#${id}`} + > + {title} + </a> + </li> + ))} + </ul> + </> + ) : null} + <div + className={cn( + "mx-4 grid gap-2 py-4", + hasHeadings && "nextra-border border-t" + )} + > + {editUrl ? ( + <a + className={cn("focus-visible:nextra-focus", tocLink)} + href={editUrl} + rel="noreferrer" + target="_blank" + > + Edit this page on GitHub  + <LinkArrowIcon + className="inline shrink-0 align-baseline" + height="1em" + /> + </a> + ) : null} + <button + aria-hidden={activeIndex < 2 ? "true" : undefined} + className={cn( + "cursor-pointer transition focus-visible:nextra-focus", + "flex items-center gap-1.5", + "whitespace-nowrap", + activeIndex < 2 ? "opacity-0" : "opacity-100", + tocLink + )} + disabled={activeIndex < 2} + onClick={scrollToTop} + type="button" + > + Scroll to top + <ArrowRightIcon + className="-rotate-90 rounded-full border border-current" + height="1.1em" + /> + </button> + </div> + </div> + ); +} + +/** + * Marks the heading nearest the top of the viewport as active. Same window + * as before: below the navbar, above the bottom 80% of the viewport. + */ +function useHeadingObserver(toc: TocEntry[]) { + useEffect(() => { + if (toc.length === 0) { + return; + } + const navbarHeight = + getComputedStyle(document.documentElement).getPropertyValue( + "--nextra-navbar-height" + ) || "0%"; + const observer = new IntersectionObserver( + (entries) => { + const entry = entries.find((e) => e.isIntersecting); + const id = entry?.target.getAttribute("data-toc-id"); + if (id) { + setActiveAnchor(id); + } + }, + { rootMargin: `-${navbarHeight.trim()} 0% -80%` } + ); + for (const { id } of toc) { + const heading = document.getElementById(id); + const target = + heading?.querySelector<HTMLElement>("a.subheading-anchor") ?? heading; + if (target) { + target.setAttribute("data-toc-id", id); + observer.observe(target); + } + } + return () => { + observer.disconnect(); + setActiveAnchor(""); + }; + }, [toc]); +} + +/* -------------------------------------------------------------------- shell */ + +const DATE_FORMAT = new Intl.DateTimeFormat("en", { + day: "numeric", + month: "long", + // Production renders the date on its UTC servers and never reformats it. + timeZone: "UTC", + year: "numeric", +}); + +export type DocsPageShellProps = { + /** Show the breadcrumb above the page. */ + breadcrumb?: boolean; + children: ReactNode; + /** URL of the page source, for "Edit this page on GitHub". */ + editUrl?: string; + /** ISO date of the last change to the page. */ + lastModified?: string | null; + /** + * "full" drops the table of contents column when there is no table of + * contents; "default" keeps the column so the page width does not change. + */ + layout?: "default" | "full"; + /** URL that returns the page as Markdown. Omit to hide "Copy page". */ + markdownUrl?: string; + pagination?: boolean; + /** Show the table of contents column content. */ + toc?: boolean; + tocItems?: TocEntry[]; +}; + +const NO_TOC: TocEntry[] = []; + +/** + * Everything around the page content: breadcrumb, "Copy page", the "last + * updated" line, previous/next links and the table of contents column. + */ +export function DocsPageShell({ + breadcrumb = true, + children, + editUrl, + lastModified, + layout = "default", + markdownUrl, + pagination = true, + toc = true, + tocItems = NO_TOC, +}: DocsPageShellProps) { + const directories = use(DirectoriesContext); + const route = usePathname() || "/"; + const active = findActive(directories, route); + const items = toc ? tocItems : NO_TOC; + useHeadingObserver(items); + + const date = lastModified ? new Date(lastModified) : null; + + return ( + <> + {layout === "default" || toc ? ( + <nav + aria-label="table of contents" + className="nextra-toc order-last w-64 shrink-0 max-xl:hidden print:hidden" + > + {toc ? <TOC editUrl={editUrl} toc={items} /> : null} + </nav> + ) : null} + <article + className={cn( + "min-h-[calc(100vh-var(--nextra-navbar-height))] w-full min-w-0 break-words", + "px-4 pt-4 pb-8 text-slate-700 md:px-12 dark:text-slate-200" + )} + > + {breadcrumb && active.path.length > 0 ? ( + <Breadcrumb path={active.path} /> + ) : null} + {markdownUrl ? <PageActions markdownUrl={markdownUrl} /> : null} + <div id="nextra-skip-nav" /> + <main data-pagefind-body>{children}</main> + {date ? ( + <div className="mt-12 mb-8 text-end text-gray-600 text-xs dark:text-gray-400"> + Last updated on{" "} + <time dateTime={date.toISOString()} suppressHydrationWarning> + {DATE_FORMAT.format(date)} + </time> + </div> + ) : ( + <div className="mt-16" /> + )} + {pagination ? ( + <Pagination next={active.next} prev={active.prev} /> + ) : null} + </article> + </> + ); +} diff --git a/components/chrome/select.tsx b/components/chrome/select.tsx new file mode 100644 index 000000000..0a877b57f --- /dev/null +++ b/components/chrome/select.tsx @@ -0,0 +1,324 @@ +"use client"; + +import { cn } from "@arcadeai/design-system/lib/utils"; +import { + type CSSProperties, + type KeyboardEvent as ReactKeyboardEvent, + type MouseEvent as ReactMouseEvent, + type ReactNode, + useCallback, + useEffect, + useId, + useLayoutEffect, + useRef, + useState, +} from "react"; +import { createPortal } from "react-dom"; +import { CheckIcon } from "@/components/chrome/icons"; + +export type SelectOption = { + id: string; + name: ReactNode; +}; + +type SelectProps = { + /** Where the options open relative to the button. */ + anchor: "top start" | "bottom start" | "bottom end"; + className?: string; + onChange: (id: string) => void; + options: SelectOption[]; + /** Content of the trigger button. */ + selectedOptionName: ReactNode; + title?: string; + /** Selected option id. Leave empty for menus that only run an action. */ + value: string; +}; + +/** Distance between the trigger and the options. */ +const ANCHOR_GAP = 10; +const VIEWPORT_PADDING = 0; +const NAVIGATION_KEYS = new Set(["ArrowDown", "ArrowUp", "Home", "End"]); + +type Position = { + left: number; + maxHeight: number; + top: number; + width: number; +}; + +function nextIndex(key: string, current: number, count: number): number { + if (key === "Home") { + return 0; + } + if (key === "End") { + return count - 1; + } + if (key === "ArrowDown") { + return Math.min(count - 1, current + 1); + } + return Math.max(0, current - 1); +} + +function optionIndex(element: HTMLElement): number { + return Number(element.dataset.index ?? -1); +} + +/** + * Button + listbox used by the theme switch, the language switch and the + * "Copy page" menu. Options render in a portal next to the trigger. + */ +export function Select({ + anchor, + className, + onChange, + options, + selectedOptionName, + title, + value, +}: SelectProps) { + const [open, setOpen] = useState(false); + const [activeIndex, setActiveIndex] = useState(-1); + const [position, setPosition] = useState<Position | null>(null); + const buttonRef = useRef<HTMLButtonElement>(null); + const listRef = useRef<HTMLDivElement>(null); + const id = useId(); + const buttonId = `${id}-button`; + const listId = `${id}-options`; + + const close = useCallback((restoreFocus: boolean) => { + setOpen(false); + setPosition(null); + if (restoreFocus) { + buttonRef.current?.focus(); + } + }, []); + + const openList = useCallback(() => { + setActiveIndex(options.findIndex((option) => option.id === value)); + setOpen(true); + }, [options, value]); + + const place = useCallback(() => { + const button = buttonRef.current; + const list = listRef.current; + if (!(button && list)) { + return; + } + const rect = button.getBoundingClientRect(); + const listWidth = Math.max(list.offsetWidth, rect.width); + const listHeight = list.scrollHeight + 2; + const above = anchor.startsWith("top"); + const available = above + ? rect.top - ANCHOR_GAP - VIEWPORT_PADDING + : window.innerHeight - rect.bottom - ANCHOR_GAP - VIEWPORT_PADDING; + const height = Math.min(listHeight, available); + const top = above + ? rect.top - ANCHOR_GAP - height + : rect.bottom + ANCHOR_GAP; + const rawLeft = anchor.endsWith("end") ? rect.right - listWidth : rect.left; + const left = Math.min( + Math.max(0, rawLeft), + Math.max(0, document.documentElement.clientWidth - listWidth) + ); + setPosition({ + left: left + window.scrollX, + maxHeight: available, + top: top + window.scrollY, + width: rect.width, + }); + }, [anchor]); + + useLayoutEffect(() => { + if (open) { + place(); + listRef.current?.focus({ preventScroll: true }); + } + }, [open, place]); + + useEffect(() => { + if (!open) { + return; + } + const onPointerDown = (event: PointerEvent) => { + const target = event.target as Node; + if ( + buttonRef.current?.contains(target) || + listRef.current?.contains(target) + ) { + return; + } + close(false); + }; + window.addEventListener("pointerdown", onPointerDown); + window.addEventListener("resize", place); + window.addEventListener("scroll", place, true); + return () => { + window.removeEventListener("pointerdown", onPointerDown); + window.removeEventListener("resize", place); + window.removeEventListener("scroll", place, true); + }; + }, [open, close, place]); + + const select = useCallback( + (index: number) => { + const option = options[index]; + if (option) { + onChange(option.id); + close(true); + } + }, + [options, onChange, close] + ); + + const onListKeyDown = useCallback( + (event: ReactKeyboardEvent<HTMLDivElement>) => { + if (event.key === "Escape" || event.key === "Tab") { + event.preventDefault(); + close(true); + return; + } + if (event.key === "Enter" || event.key === " ") { + event.preventDefault(); + select(activeIndex); + return; + } + if (!NAVIGATION_KEYS.has(event.key)) { + return; + } + event.preventDefault(); + setActiveIndex((current) => + nextIndex(event.key, current, options.length) + ); + }, + [activeIndex, close, options.length, select] + ); + + const onButtonKeyDown = useCallback( + (event: ReactKeyboardEvent<HTMLButtonElement>) => { + if (event.key === "ArrowDown" || event.key === "ArrowUp") { + event.preventDefault(); + openList(); + } + // Space opens the list; Enter is left alone, as before. + if (event.key === "Enter") { + event.preventDefault(); + } + }, + [openList] + ); + + const onButtonClick = useCallback(() => { + if (open) { + close(false); + } else { + openList(); + } + }, [open, close, openList]); + + const onOptionClick = useCallback( + (event: ReactMouseEvent<HTMLDivElement>) => { + select(optionIndex(event.currentTarget)); + }, + [select] + ); + + const onOptionKeyDown = useCallback( + (event: ReactKeyboardEvent<HTMLDivElement>) => { + if (event.key === "Enter") { + select(optionIndex(event.currentTarget)); + } + }, + [select] + ); + + const onOptionPointerMove = useCallback( + (event: ReactMouseEvent<HTMLDivElement>) => { + setActiveIndex(optionIndex(event.currentTarget)); + }, + [] + ); + + const style: CSSProperties = { + left: position?.left ?? 0, + maxHeight: position + ? `min(var(--anchor-max-height, 100vh), ${position.maxHeight}px)` + : undefined, + maxWidth: "100vw", + overflow: "auto", + position: "absolute", + top: position?.top ?? 0, + visibility: position ? undefined : "hidden", + ["--button-width" as string]: `${position?.width ?? 0}px`, + }; + + return ( + <> + <button + aria-controls={open ? listId : undefined} + aria-expanded={open} + aria-haspopup="listbox" + className={cn( + "h-7 cursor-pointer rounded-[6px] px-2 font-medium text-xs transition-colors focus-visible:nextra-focus", + open + ? "bg-gray-200 text-gray-900 dark:bg-primary-100/10 dark:text-gray-50" + : "text-gray-600 hover:bg-gray-200 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-primary-100/5 dark:hover:text-gray-50", + className + )} + id={buttonId} + onClick={onButtonClick} + onKeyDown={onButtonKeyDown} + ref={buttonRef} + title={title} + type="button" + > + {selectedOptionName} + </button> + {open + ? createPortal( + <div + aria-labelledby={buttonId} + aria-orientation="vertical" + className={cn( + "focus-visible:nextra-focus", + "z-30 max-h-64 min-w-(--button-width) rounded-[6px] border border-black/5 bg-nextra-bg/70 py-1 text-sm shadow-lg backdrop-blur-md dark:border-white/20" + )} + data-anchor={anchor} + id={listId} + onKeyDown={onListKeyDown} + ref={listRef} + role="listbox" + style={style} + tabIndex={0} + > + {options.map((option, index) => { + const isSelected = option.id === value; + return ( + <div + aria-selected={isSelected} + className={cn( + index === activeIndex + ? "bg-primary-100 text-primary-800 dark:bg-primary-500/10 dark:text-primary-500" + : "text-gray-800 dark:text-gray-100", + "cursor-pointer whitespace-nowrap px-3 py-1.5 transition-colors", + isSelected && "flex items-center justify-between gap-3" + )} + data-index={index} + key={option.id} + onClick={onOptionClick} + onKeyDown={onOptionKeyDown} + onPointerMove={onOptionPointerMove} + role="option" + tabIndex={-1} + > + {option.name} + {isSelected ? <CheckIcon height="1em" /> : null} + </div> + ); + })} + </div>, + document.body + ) + : null} + </> + ); +} diff --git a/components/chrome/sidebar-model.ts b/components/chrome/sidebar-model.ts new file mode 100644 index 000000000..ca5e4b98e --- /dev/null +++ b/components/chrome/sidebar-model.ts @@ -0,0 +1,107 @@ +import type * as PageTree from "fumadocs-core/page-tree"; +import type { ReactNode } from "react"; + +/** + * Plain, serialisable description of the docs sidebar. Built on the server + * from the Fumadocs page tree (lib/source.ts) and rendered by + * components/chrome/sidebar.tsx. + */ +export type SidebarNode = SidebarFolder | SidebarPage | SidebarSeparator; + +export type SidebarPage = { + href: string; + icon?: ReactNode; + id: string; + kind: "page"; + /** + * True for entries that only link somewhere (toolkit pages, cross links) + * instead of standing for a docs file. They are skipped by previous/next. + */ + linkOnly?: boolean; + title: ReactNode; +}; + +export type SidebarFolder = { + children: SidebarNode[]; + icon?: ReactNode; + id: string; + kind: "folder"; + /** URL prefix the folder stands for, e.g. `/en/operate/governance`. */ + route: string; + title: ReactNode; +}; + +export type SidebarSeparator = { + id: string; + kind: "separator"; + title?: ReactNode; +}; + +const TRAILING_INDEX = /\/index$/; + +function folderRoute( + node: PageTree.Folder, + baseUrl: string, + parentRoute: string, + position: number +): string { + if (node.index) { + return node.index.url; + } + const folderPath = node.$ref?.folder; + if (folderPath) { + return `${baseUrl}/${folderPath}`.replace(TRAILING_INDEX, ""); + } + return `${parentRoute}/__folder-${position}`; +} + +function toPage(item: PageTree.Item, fallbackId: string): SidebarPage { + return { + href: item.url, + icon: item.icon, + id: item.$id ?? fallbackId, + kind: "page", + linkOnly: item.$ref === undefined ? true : undefined, + title: item.name, + }; +} + +function toNodes( + nodes: PageTree.Node[], + baseUrl: string, + parentRoute: string +): SidebarNode[] { + return nodes.map((node, position): SidebarNode => { + const fallbackId = `${parentRoute}#${position}`; + if (node.type === "separator") { + return { + id: node.$id ?? fallbackId, + kind: "separator", + title: node.name, + }; + } + if (node.type === "page") { + return toPage(node, fallbackId); + } + const route = folderRoute(node, baseUrl, parentRoute, position); + const children = toNodes(node.children, baseUrl, route); + return { + // A folder's own page is listed as its first entry. + children: node.index + ? [toPage(node.index, `${route}#index`), ...children] + : children, + icon: node.icon, + id: node.$id ?? fallbackId, + kind: "folder", + route, + title: node.name, + }; + }); +} + +export function buildSidebarModel( + tree: PageTree.Root, + baseUrl: string +): SidebarNode[] { + return toNodes(tree.children, baseUrl, baseUrl); +} diff --git a/components/chrome/sidebar.tsx b/components/chrome/sidebar.tsx new file mode 100644 index 000000000..1a2c9ae07 --- /dev/null +++ b/components/chrome/sidebar.tsx @@ -0,0 +1,388 @@ +"use client"; + +import { cn } from "@arcadeai/design-system/lib/utils"; +import { useSidebar } from "fumadocs-ui/components/sidebar/base"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { + type FocusEventHandler, + type ReactNode, + type RefObject, + useCallback, + useEffect, + useId, + useRef, + useState, +} from "react"; +import { AlgoliaSearch } from "@/components/algolia-search"; +import { Collapse } from "@/components/chrome/collapse"; +import { + ArrowRightIcon, + ExpandIcon, + LinkArrowIcon, +} from "@/components/chrome/icons"; +import type { + SidebarFolder, + SidebarNode, + SidebarPage, +} from "@/components/chrome/sidebar-model"; +import { setFocusedRoute, useFocusedRoute } from "@/components/chrome/stores"; +import { LocaleSwitch, ThemeSwitch } from "@/components/chrome/switchers"; + +/** Folders below this level start closed. */ +const DEFAULT_MENU_COLLAPSE_LEVEL = 2; +const MOBILE_BREAKPOINT = 767; + +/** Open/closed state the reader chose, kept across client navigations. */ +const TreeState: Record<string, boolean> = Object.create(null); + +const classes = { + active: cn( + "bg-primary-100 font-semibold text-primary-800 dark:bg-primary-400/10 dark:text-primary-600", + "contrast-more:border-primary-500!" + ), + border: cn( + "relative before:absolute before:inset-y-1", + 'before:w-px before:bg-gray-200 before:content-[""] dark:before:bg-neutral-800', + "ms-3 ps-3 pt-1 before:start-0" + ), + footer: cn( + "nextra-sidebar-footer nextra-border mx-4 flex items-center gap-2 border-t py-4" + ), + inactive: cn( + "text-gray-600 hover:bg-gray-100 hover:text-gray-900", + "dark:text-neutral-400 dark:hover:bg-primary-100/5 dark:hover:text-gray-50", + "contrast-more:text-gray-900 contrast-more:dark:text-gray-50", + "contrast-more:border-transparent contrast-more:hover:border-gray-900 contrast-more:dark:hover:border-gray-50" + ), + link: cn( + "flex rounded px-2 py-1.5 text-sm transition-colors [word-break:break-word]", + "cursor-pointer contrast-more:border" + ), + list: cn("grid gap-1"), + wrapper: cn("nextra-scrollbar nextra-mask overflow-y-auto p-4"), +}; + +/** Top-level entries show their section icon next to a stronger label. */ +function Title({ icon, title }: { icon?: ReactNode; title: ReactNode }) { + if (!icon) { + return title; + } + return ( + <span className="flex items-center gap-2 font-medium text-gray-950 dark:text-white [&>svg]:size-4"> + {icon} + {title} + </span> + ); +} + +const onFocus: FocusEventHandler<HTMLAnchorElement> = (event) => { + const route = event.currentTarget.getAttribute("href"); + if (route) { + setFocusedRoute(route); + } +}; + +const onBlur: FocusEventHandler<HTMLAnchorElement> = () => { + setFocusedRoute(""); +}; + +function useActiveRoute(): string { + return usePathname() || "/"; +} + +function Folder({ item, level }: { item: SidebarFolder; level: number }) { + const route = useActiveRoute(); + const active = [route, `${route}/`].includes(`${item.route}/`); + const activeRouteInside = active || route.startsWith(`${item.route}/`); + const focusedRoute = useFocusedRoute(); + const focusedRouteInside = focusedRoute.startsWith(`${item.route}/`); + const [, rerender] = useState({}); + + const state = TreeState[item.route]; + const open = + state === undefined + ? active || + activeRouteInside || + focusedRouteInside || + level < DEFAULT_MENU_COLLAPSE_LEVEL + : state || focusedRouteInside; + + useEffect(() => { + // Folders the reader navigated away from go back to their default state. + if (activeRouteInside && focusedRouteInside) { + TreeState[item.route] = true; + } else { + delete TreeState[item.route]; + } + }, [activeRouteInside, focusedRouteInside, item.route]); + + const toggle = useCallback(() => { + TreeState[item.route] = !open; + rerender({}); + }, [item.route, open]); + + return ( + <li className={cn({ active, open }) || undefined}> + <button + className={cn( + "cursor-pointer transition focus-visible:nextra-focus", + "items-center justify-between gap-2", + "w-full text-start", + classes.link, + active ? classes.active : classes.inactive + )} + data-href={item.route} + onClick={toggle} + type="button" + > + <Title icon={item.icon} title={item.title} /> + <ArrowRightIcon + className={cn( + "shrink-0", + "rounded-[4px] p-0.5 hover:bg-gray-800/5 dark:hover:bg-gray-100/5", + "origin-center transition-all motion-reduce:transition-none rtl:-rotate-180", + open && "ltr:rotate-90 rtl:-rotate-270" + )} + height="18" + /> + </button> + <Collapse isOpen={open}> + <Menu + className={classes.border} + directories={item.children} + level={level} + /> + </Collapse> + </li> + ); +} + +function File({ item }: { item: SidebarPage }) { + const route = useActiveRoute(); + const { setOpen } = useSidebar(); + const active = [route, `${route}/`].includes(`${item.href}/`); + const className = cn( + "focus-visible:nextra-focus", + classes.link, + active ? classes.active : classes.inactive + ); + const closeMenu = useCallback(() => setOpen(false), [setOpen]); + + if (item.href.startsWith("http")) { + return ( + <li className={active ? "active" : undefined}> + <a + className={className} + href={item.href} + onBlur={onBlur} + onClick={closeMenu} + onFocus={onFocus} + rel="noreferrer" + target="_blank" + > + <Title icon={item.icon} title={item.title} /> + {typeof item.title === "string" && !item.icon ? ( + <> +   + <LinkArrowIcon + className="inline shrink-0 align-baseline" + height="1em" + /> + </> + ) : null} + </a> + </li> + ); + } + + return ( + <li className={active ? "active" : undefined}> + <Link + className={className} + href={item.href} + onBlur={onBlur} + onClick={closeMenu} + onFocus={onFocus} + prefetch={false} + > + <Title icon={item.icon} title={item.title} /> + </Link> + </li> + ); +} + +function Menu({ + className, + directories, + level, + ref, +}: { + className?: string; + directories: SidebarNode[]; + level: number; + ref?: RefObject<HTMLUListElement | null>; +}) { + return ( + <ul className={cn(classes.list, className)} ref={ref}> + {directories.map((item) => { + if (item.kind === "separator") { + return ( + <li + className={ + item.title + ? "not-first:mt-5 mb-2 px-2 py-1.5 font-semibold text-gray-900 text-sm dark:text-gray-100" + : "my-4" + } + key={item.id} + > + {item.title || <hr className="nextra-border mx-2 border-t" />} + </li> + ); + } + if (item.kind === "folder") { + return <Folder item={item} key={item.id} level={level + 1} />; + } + return <File item={item} key={item.id} />; + })} + </ul> + ); +} + +/** Centers the active entry in a scrollable sidebar list. */ +function scrollActiveIntoView(container: HTMLElement | null) { + const activeLink = container?.querySelector<HTMLElement>("li.active"); + if (!(container && activeLink)) { + return; + } + const containerRect = container.getBoundingClientRect(); + const linkRect = activeLink.getBoundingClientRect(); + container.scrollTop += + linkRect.top - + containerRect.top - + (container.clientHeight - linkRect.height) / 2; +} + +/** Full-screen menu shown on small screens when the navbar menu button is open. */ +export function MobileNav({ directories }: { directories: SidebarNode[] }) { + const { open } = useSidebar(); + const sidebarRef = useRef<HTMLUListElement>(null); + + useEffect(() => { + document.documentElement.classList.toggle("max-md:overflow-hidden", open); + if (open) { + scrollActiveIntoView(sidebarRef.current); + } + return () => { + document.documentElement.classList.remove("max-md:overflow-hidden"); + }; + }, [open]); + + return ( + <aside + className={cn( + "nextra-mobile-nav", + "flex flex-col", + "fixed inset-0 z-20 overscroll-contain pt-(--nextra-navbar-height)", + "[contain:layout_style]", + "md:hidden", + "bg-nextra-bg", + open + ? "[transform:translate3d(0,0,0)]" + : "[transform:translate3d(0,-100%,0)]" + )} + > + <div className="px-4 pt-4"> + <AlgoliaSearch /> + </div> + <Menu + className={classes.wrapper} + directories={directories} + level={0} + ref={sidebarRef} + /> + <div className={cn(classes.footer, "mt-auto")}> + <ThemeSwitch className="grow" /> + <LocaleSwitch className="grow justify-end" /> + </div> + </aside> + ); +} + +/** Desktop sidebar: the page tree plus the language, theme and collapse controls. */ +export function Sidebar({ directories }: { directories: SidebarNode[] }) { + const [isExpanded, setIsExpanded] = useState(true); + const [showToggleAnimation, setToggleAnimation] = useState(false); + const sidebarRef = useRef<HTMLDivElement>(null); + const sidebarControlsId = useId(); + + useEffect(() => { + if (window.innerWidth > MOBILE_BREAKPOINT) { + scrollActiveIntoView(sidebarRef.current); + } + }, []); + + const toggleExpanded = useCallback(() => { + setIsExpanded((prev) => !prev); + setToggleAnimation(true); + }, []); + + return ( + <aside + className={cn( + "nextra-sidebar print:hidden", + "transition-all ease-in-out", + "flex flex-col max-md:hidden", + "h-[calc(100dvh-var(--nextra-navbar-height))]", + "top-(--nextra-navbar-height) shrink-0", + isExpanded ? "w-64" : "w-20", + "sticky" + )} + id={sidebarControlsId} + > + <div + className={cn(classes.wrapper, "grow", !isExpanded && "no-scrollbar")} + ref={sidebarRef} + > + <Collapse horizontal isOpen={isExpanded}> + <Menu directories={directories} level={0} /> + </Collapse> + </div> + <div + className={cn( + "sticky bottom-0 bg-nextra-bg", + classes.footer, + !isExpanded && "flex-wrap justify-center", + showToggleAnimation && [ + "*:opacity-0", + isExpanded + ? "*:animate-[fade-in_1s_ease_.2s_forwards]" + : "*:animate-[fade-in2_1s_ease_.2s_forwards]", + ] + )} + > + <LocaleSwitch className={isExpanded ? "grow" : ""} lite={!isExpanded} /> + <ThemeSwitch lite /> + <button + aria-controls={sidebarControlsId} + aria-expanded={isExpanded} + className={cn( + "cursor-pointer transition focus-visible:nextra-focus", + "rounded-[6px] p-2", + "text-gray-600 hover:bg-gray-200 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-primary-100/5 dark:hover:text-gray-50" + )} + onClick={toggleExpanded} + title={isExpanded ? "Collapse sidebar" : "Expand sidebar"} + type="button" + > + <ExpandIcon + className={cn( + !isExpanded && "*:first:origin-[35%] *:first:rotate-180" + )} + height="12" + /> + </button> + </div> + </aside> + ); +} diff --git a/components/chrome/site-footer.tsx b/components/chrome/site-footer.tsx new file mode 100644 index 000000000..03975ade4 --- /dev/null +++ b/components/chrome/site-footer.tsx @@ -0,0 +1,13 @@ +import { Footer } from "@/components/footer"; + +/** Footer band shown under every page. */ +export function SiteFooter() { + return ( + <div className="bg-gray-100 pb-[env(safe-area-inset-bottom)] dark:bg-neutral-900 print:bg-transparent"> + <hr className="nextra-border" /> + <footer className="mx-auto flex max-w-(--nextra-content-width) justify-center py-12 pr-[max(env(safe-area-inset-right),1.5rem)] pl-[max(env(safe-area-inset-left),1.5rem)] text-gray-600 md:justify-start dark:text-gray-400"> + <Footer /> + </footer> + </div> + ); +} diff --git a/components/chrome/stores.ts b/components/chrome/stores.ts new file mode 100644 index 000000000..beeb7dbab --- /dev/null +++ b/components/chrome/stores.ts @@ -0,0 +1,63 @@ +"use client"; + +import { createContext, type ReactNode, useSyncExternalStore } from "react"; +import type { SidebarNode } from "@/components/chrome/sidebar-model"; + +/** + * Small client stores shared by the chrome components: the heading currently + * in view and the sidebar route that has keyboard focus. + */ + +/** Sidebar entries, provided by the docs layout. */ +export const DirectoriesContext = createContext<SidebarNode[]>([]); + +export type TocEntry = { + depth: number; + id: string; + title: ReactNode; +}; + +function createStore<T>(initial: T) { + let value = initial; + const listeners = new Set<() => void>(); + return { + get: () => value, + set: (next: T) => { + if (Object.is(next, value)) { + return; + } + value = next; + for (const listener of listeners) { + listener(); + } + }, + subscribe: (listener: () => void) => { + listeners.add(listener); + return () => { + listeners.delete(listener); + }; + }, + }; +} + +const activeAnchorStore = createStore(""); +const focusedRouteStore = createStore(""); + +export const setActiveAnchor = activeAnchorStore.set; +export const setFocusedRoute = focusedRouteStore.set; + +export function useActiveAnchor(): string { + return useSyncExternalStore( + activeAnchorStore.subscribe, + activeAnchorStore.get, + () => "" + ); +} + +export function useFocusedRoute(): string { + return useSyncExternalStore( + focusedRouteStore.subscribe, + focusedRouteStore.get, + () => "" + ); +} diff --git a/components/chrome/switchers.tsx b/components/chrome/switchers.tsx new file mode 100644 index 000000000..fb31d3fbc --- /dev/null +++ b/components/chrome/switchers.tsx @@ -0,0 +1,116 @@ +"use client"; + +import { cn } from "@arcadeai/design-system/lib/utils"; +import { usePathname } from "next/navigation"; +import { useTheme } from "next-themes"; +import { useCallback, useEffect, useState } from "react"; +import { GlobeIcon, MoonIcon, SunIcon } from "@/components/chrome/icons"; +import { Select } from "@/components/chrome/select"; + +const THEME_LABELS = { + dark: "Dark", + light: "Light", + system: "System", +} as const; + +type ThemeId = keyof typeof THEME_LABELS; + +const THEME_OPTIONS = [ + { id: "light", name: THEME_LABELS.light }, + { id: "dark", name: THEME_LABELS.dark }, + { id: "system", name: THEME_LABELS.system }, +]; + +/** Docs are English-only; the switch is kept so the sidebar footer is unchanged. */ +const LOCALES = [{ locale: "en", name: "English" }]; +const LOCALE_OPTIONS = LOCALES.map((l) => ({ id: l.locale, name: l.name })); +const CURRENT_LOCALE = "en"; +const ONE_YEAR = 31_536_000_000; + +function useMounted(): boolean { + const [mounted, setMounted] = useState(false); + useEffect(() => { + setMounted(true); + }, []); + return mounted; +} + +function isThemeId(value: string | undefined): value is ThemeId { + return value === "dark" || value === "light" || value === "system"; +} + +type SwitchProps = { + className?: string; + /** Icon only, no label. */ + lite?: boolean; +}; + +export function ThemeSwitch({ lite, className }: SwitchProps) { + const { setTheme, resolvedTheme, theme } = useTheme(); + const mounted = useMounted(); + const Icon = mounted && resolvedTheme === "dark" ? MoonIcon : SunIcon; + const id: ThemeId = mounted && isThemeId(theme) ? theme : "light"; + + return ( + <Select + anchor="top start" + className={cn("flex items-center gap-2", className)} + onChange={setTheme} + options={THEME_OPTIONS} + selectedOptionName={ + <> + <Icon height="12" /> + {lite ? null : THEME_LABELS[id]} + </> + } + title="Change theme" + value={id} + /> + ); +} + +export function LocaleSwitch({ lite, className }: SwitchProps) { + const pathname = usePathname(); + + const onChange = useCallback( + (lang: string) => { + const go = () => { + window.location.href = pathname.replace( + `/${CURRENT_LOCALE}`, + `/${lang}` + ); + }; + // Same cookie the previous theme wrote when a language was picked. + if ("cookieStore" in window) { + window.cookieStore + .set({ + expires: Date.now() + ONE_YEAR, + name: "NEXT_LOCALE", + path: "/", + value: lang, + }) + .then(go, go); + return; + } + go(); + }, + [pathname] + ); + + return ( + <Select + anchor="top start" + className={cn("flex items-center gap-2", className)} + onChange={onChange} + options={LOCALE_OPTIONS} + selectedOptionName={ + <> + <GlobeIcon height="12" /> + {lite ? null : LOCALES.find((l) => l.locale === CURRENT_LOCALE)?.name} + </> + } + title="Change language" + value={CURRENT_LOCALE} + /> + ); +} diff --git a/app/_components/coming-soon-context.tsx b/components/coming-soon-context.tsx similarity index 100% rename from app/_components/coming-soon-context.tsx rename to components/coming-soon-context.tsx diff --git a/app/_components/contact-email.tsx b/components/contact-email.tsx similarity index 100% rename from app/_components/contact-email.tsx rename to components/contact-email.tsx diff --git a/components/custom-layout.tsx b/components/custom-layout.tsx new file mode 100644 index 000000000..488f48bb9 --- /dev/null +++ b/components/custom-layout.tsx @@ -0,0 +1,14 @@ +import type React from "react"; +import { PlaceholderReplacer } from "@/components/placeholder-replacer"; +import { OrySessionProvider } from "@/lib/ory-session-context"; + +const CustomLayout: React.FC<{ children: React.ReactNode }> = ({ + children, +}) => ( + <OrySessionProvider> + <PlaceholderReplacer /> + {children} + </OrySessionProvider> +); + +export default CustomLayout; diff --git a/app/_components/dashboard-link.tsx b/components/dashboard-link.tsx similarity index 100% rename from app/_components/dashboard-link.tsx rename to components/dashboard-link.tsx diff --git a/app/_components/dynamic-survey/hooks/use-survey.ts b/components/dynamic-survey/hooks/use-survey.ts similarity index 100% rename from app/_components/dynamic-survey/hooks/use-survey.ts rename to components/dynamic-survey/hooks/use-survey.ts diff --git a/app/_components/dynamic-survey/index.tsx b/components/dynamic-survey/index.tsx similarity index 100% rename from app/_components/dynamic-survey/index.tsx rename to components/dynamic-survey/index.tsx diff --git a/components/dynamic-survey/multiple-choice-question.tsx b/components/dynamic-survey/multiple-choice-question.tsx new file mode 100644 index 000000000..f48759c56 --- /dev/null +++ b/components/dynamic-survey/multiple-choice-question.tsx @@ -0,0 +1,134 @@ +"use client"; +import { Checkbox, Input, Label } from "@arcadeai/design-system"; +import type { MultipleSurveyQuestion } from "posthog-js"; +import { useCallback, useMemo, useState } from "react"; + +type MultipleChoiceQuestionProps = { + previousResponse?: string[]; + question: MultipleSurveyQuestion; +}; + +type ChoiceCheckboxProps = { + choice: string; + defaultChecked?: boolean; + onChange: (choice: string, checked: boolean) => void; + required: boolean; +}; + +function ChoiceCheckbox({ + choice, + defaultChecked, + onChange, + required, +}: ChoiceCheckboxProps) { + const handleCheckedChange = useCallback( + (checked: boolean) => onChange(choice, checked), + [choice, onChange] + ); + return ( + <div className="flex items-center space-x-2"> + <Checkbox + defaultChecked={defaultChecked} + id={choice} + name={choice} + onCheckedChange={handleCheckedChange} + required={required} + /> + <Label htmlFor={choice}>{choice}</Label> + </div> + ); +} + +export function MultipleChoiceQuestion({ + question, + previousResponse, +}: MultipleChoiceQuestionProps) { + const hasOtherInChoices = question.choices.some( + (choice) => choice.toLowerCase() === "other" + ); + + const [selectedChoices, setSelectedChoices] = useState<string[]>( + previousResponse || [] + ); + + const shuffledChoices = useMemo(() => { + if (!(question.choices.length && question.shuffleOptions)) { + return question.choices; + } + + const [others, regular] = question.choices.reduce<[string[], string[]]>( + ([o, r], choice) => + choice.toLowerCase() === "other" + ? [[...o, choice], r] + : [o, [...r, choice]], + [[], []] + ); + + const ShuffleRandomOffset = 0.5; + return [ + ...regular.sort(() => Math.random() - ShuffleRandomOffset), + ...others, + ]; + }, [question.choices, question.shuffleOptions]); + + const handleCheckboxChange = useCallback( + (choice: string, checked: boolean) => { + setSelectedChoices((prev) => { + if (checked) { + return [...prev, choice]; + } + return prev.filter((c) => c !== choice); + }); + }, + [] + ); + + const handleOtherChange = useCallback( + (checked: boolean) => handleCheckboxChange("other", checked), + [handleCheckboxChange] + ); + + const hasOtherSelected = hasOtherInChoices + ? selectedChoices.some((choice) => choice.toLowerCase() === "other") + : selectedChoices.includes("other"); + + return ( + <div className="space-y-3"> + {shuffledChoices.map((choice) => ( + <ChoiceCheckbox + choice={choice} + defaultChecked={previousResponse?.includes(choice)} + key={choice} + onChange={handleCheckboxChange} + required={!question.optional && selectedChoices.length === 0} + /> + ))} + {question.hasOpenChoice && !hasOtherInChoices ? ( + <div className="flex items-center space-x-2"> + <Checkbox + checked={hasOtherSelected} + id="other" + onCheckedChange={handleOtherChange} + /> + <Label htmlFor="other">Other</Label> + </div> + ) : null} + {question.hasOpenChoice && hasOtherSelected ? ( + <div className="mt-4"> + <Input + className="mt-1" + defaultValue={ + previousResponse?.find( + (choice) => !question.choices.includes(choice) + ) || "" + } + id="other" + name="other" + placeholder="Please specify" + required={!question.optional} + /> + </div> + ) : null} + </div> + ); +} diff --git a/app/_components/dynamic-survey/navigation.tsx b/components/dynamic-survey/navigation.tsx similarity index 100% rename from app/_components/dynamic-survey/navigation.tsx rename to components/dynamic-survey/navigation.tsx diff --git a/app/_components/dynamic-survey/open-question.tsx b/components/dynamic-survey/open-question.tsx similarity index 100% rename from app/_components/dynamic-survey/open-question.tsx rename to components/dynamic-survey/open-question.tsx diff --git a/app/_components/dynamic-survey/progress.tsx b/components/dynamic-survey/progress.tsx similarity index 100% rename from app/_components/dynamic-survey/progress.tsx rename to components/dynamic-survey/progress.tsx diff --git a/app/_components/dynamic-survey/question.tsx b/components/dynamic-survey/question.tsx similarity index 100% rename from app/_components/dynamic-survey/question.tsx rename to components/dynamic-survey/question.tsx diff --git a/app/_components/dynamic-survey/single-choice-question.tsx b/components/dynamic-survey/single-choice-question.tsx similarity index 100% rename from app/_components/dynamic-survey/single-choice-question.tsx rename to components/dynamic-survey/single-choice-question.tsx diff --git a/app/_components/dynamic-survey/utils.ts b/components/dynamic-survey/utils.ts similarity index 100% rename from app/_components/dynamic-survey/utils.ts rename to components/dynamic-survey/utils.ts diff --git a/app/_components/early-access-registry-survey.tsx b/components/early-access-registry-survey.tsx similarity index 100% rename from app/_components/early-access-registry-survey.tsx rename to components/early-access-registry-survey.tsx diff --git a/app/_components/enhanced-code-block.tsx b/components/enhanced-code-block.tsx similarity index 100% rename from app/_components/enhanced-code-block.tsx rename to components/enhanced-code-block.tsx diff --git a/app/_components/error-hierarchy.tsx b/components/error-hierarchy.tsx similarity index 96% rename from app/_components/error-hierarchy.tsx rename to components/error-hierarchy.tsx index df6f96f1a..4b3729b6d 100644 --- a/app/_components/error-hierarchy.tsx +++ b/components/error-hierarchy.tsx @@ -1,7 +1,7 @@ export function ErrorHierarchy({ description }: { description: string }) { return ( <div className="my-6"> - <h2 className="mb-4 font-bold text-2xl">Error hierarchy</h2> + <h2 className="mt-0 mb-4 font-bold text-2xl">Error hierarchy</h2> <p className="mb-4">{description}</p> <pre className="overflow-x-auto rounded-lg bg-gray-50 p-4 text-sm dark:bg-gray-900"> <code>{` diff --git a/app/_components/footer.tsx b/components/footer.tsx similarity index 99% rename from app/_components/footer.tsx rename to components/footer.tsx index 35cf02c8a..2447b3924 100644 --- a/app/_components/footer.tsx +++ b/components/footer.tsx @@ -1,7 +1,7 @@ import { Discord, Github } from "@arcadeai/design-system"; import { Mail } from "lucide-react"; import type React from "react"; -import { urlConfig } from "@/config"; +import { urlConfig } from "@/lib/config"; import Linkedin from "./icons/linkedin"; const ArcadeLogo = () => ( diff --git a/app/_components/github-badge.tsx b/components/github-badge.tsx similarity index 100% rename from app/_components/github-badge.tsx rename to components/github-badge.tsx diff --git a/app/_components/glossary-term.tsx b/components/glossary-term.tsx similarity index 100% rename from app/_components/glossary-term.tsx rename to components/glossary-term.tsx diff --git a/app/_components/guide-overview.tsx b/components/guide-overview.tsx similarity index 100% rename from app/_components/guide-overview.tsx rename to components/guide-overview.tsx diff --git a/app/_components/icons/github.tsx b/components/icons/github.tsx similarity index 100% rename from app/_components/icons/github.tsx rename to components/icons/github.tsx diff --git a/app/_components/icons/linkedin.tsx b/components/icons/linkedin.tsx similarity index 100% rename from app/_components/icons/linkedin.tsx rename to components/icons/linkedin.tsx diff --git a/app/_components/integration-card.tsx b/components/integration-card.tsx similarity index 100% rename from app/_components/integration-card.tsx rename to components/integration-card.tsx diff --git a/app/_components/logo.tsx b/components/logo.tsx similarity index 100% rename from app/_components/logo.tsx rename to components/logo.tsx diff --git a/components/mdx/callout.tsx b/components/mdx/callout.tsx new file mode 100644 index 000000000..8154b246d --- /dev/null +++ b/components/mdx/callout.tsx @@ -0,0 +1,104 @@ +import type { ComponentProps, CSSProperties, ReactNode } from "react"; +import { cx } from "./elements"; +import { + GitHubCautionIcon, + GitHubImportantIcon, + GitHubNoteIcon, + GitHubTipIcon, + GitHubWarningIcon, +} from "./icons"; + +export type CalloutType = + | "default" + | "info" + | "warning" + | "error" + | "important"; + +const ICON_CLASS = "mt-[.3em]"; + +const TypeToEmoji: Record<CalloutType, ReactNode> = { + default: <GitHubTipIcon className={ICON_CLASS} height=".8em" />, + error: <GitHubCautionIcon className={ICON_CLASS} height=".8em" />, + important: <GitHubImportantIcon className={ICON_CLASS} height=".8em" />, + info: <GitHubNoteIcon className={ICON_CLASS} height=".8em" />, + warning: <GitHubWarningIcon className={ICON_CLASS} height=".8em" />, +}; + +const classes: Record<CalloutType, string> = { + default: cx( + "bg-green-100 dark:bg-green-900/30", + "text-green-700 dark:text-green-500", + "border-green-700 dark:border-green-800" + ), + error: cx( + "bg-red-100 dark:bg-red-900/30", + "text-red-700 dark:text-red-500", + "border-red-700 dark:border-red-600" + ), + important: cx( + "bg-purple-100 dark:bg-purple-900/30", + "text-purple-600 dark:text-purple-400", + "border-purple-600" + ), + info: cx( + "bg-blue-100 dark:bg-blue-900/30", + "text-blue-700 dark:text-blue-400", + "border-blue-700 dark:border-blue-600" + ), + warning: cx( + "bg-yellow-50 dark:bg-yellow-700/30", + "text-yellow-700 dark:text-yellow-500", + "border-yellow-700" + ), +}; + +/** + * Type names the toolkit-page rules in app/global.css select on + * (`[data-callout][data-type="warn"]`, …). + */ +const DATA_TYPE: Record<CalloutType, string> = { + default: "idea", + error: "error", + important: "important", + info: "info", + warning: "warn", +}; + +const EMOJI_STYLE: CSSProperties = { + fontFamily: '"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', +}; + +export type CalloutProps = Omit<ComponentProps<"div">, "type"> & { + type?: CalloutType | null; + emoji?: ReactNode; +}; + +/** Nextra's `<Callout>` (nextra/components), ported class for class. */ +export function Callout({ + className, + type = "default", + emoji = type ? TypeToEmoji[type] : undefined, + ...props +}: CalloutProps) { + return ( + <div + className={cx( + "nextra-callout not-first:mt-[1.25em] flex overflow-x-auto rounded-[.5rem] border py-[.5em] pe-[1em]", + "contrast-more:border-current!", + type ? classes[type] : undefined + )} + data-callout="" + data-type={type ? DATA_TYPE[type] : undefined} + > + <div + className="select-none ps-[.6em] pe-[.4em] text-[1.25em]" + data-pagefind-ignore="all" + style={EMOJI_STYLE} + > + {emoji} + </div> + <div className={cx("w-full min-w-0", className)} {...props} /> + </div> + ); +} diff --git a/components/mdx/cards.tsx b/components/mdx/cards.tsx new file mode 100644 index 000000000..256c5b228 --- /dev/null +++ b/components/mdx/cards.tsx @@ -0,0 +1,81 @@ +import NextLink from "next/link"; +import type { ComponentProps, CSSProperties, ReactNode } from "react"; +import { cx } from "./elements"; + +export type CardProps = Omit<ComponentProps<typeof NextLink>, "title"> & { + title: ReactNode; + icon?: ReactNode; + arrow?: boolean; +}; + +/** Nextra's `<Cards.Card>` (nextra/components), ported class for class. */ +export function Card({ + children, + title, + icon, + arrow, + href, + className, + ...props +}: CardProps) { + return ( + <NextLink + className={cx( + "group", + "nextra-card flex flex-col justify-start overflow-hidden rounded-[.5rem] border border-gray-200 focus-visible:nextra-focus", + "text-current no-underline dark:shadow-none", + "shadow-gray-100 hover:shadow-gray-100 dark:hover:shadow-none", + "active:shadow-gray-200 active:shadow-sm", + "transition-all duration-200 hover:border-gray-300", + children + ? "bg-gray-100 shadow hover:shadow-lg dark:border-neutral-700 dark:bg-neutral-800 dark:text-gray-50 dark:hover:border-neutral-500 dark:hover:bg-neutral-700" + : "bg-transparent shadow-sm hover:bg-slate-50 hover:shadow-md dark:border-neutral-800 dark:hover:border-neutral-700 dark:hover:bg-neutral-900", + className + )} + href={href} + {...props} + > + {children} + <span + className={cx( + "flex items-center gap-2 p-4 font-semibold text-gray-700 hover:text-gray-900", + arrow && + 'after:transition-transform after:duration-75 after:content-["→"] group-hover:after:translate-x-0.5 group-focus:after:translate-x-0.5', + children + ? "dark:text-gray-300 dark:hover:text-gray-100" + : "dark:text-neutral-200 dark:hover:text-neutral-50" + )} + title={typeof title === "string" ? title : undefined} + > + {icon} + <span className="_truncate">{title}</span> + </span> + </NextLink> + ); +} + +export type CardsProps = ComponentProps<"div"> & { + /** Maximum number of columns. */ + num?: number; +}; + +/** Nextra's `<Cards>` grid. */ +export function Cards({ + children, + num = 3, + className, + style, + ...props +}: CardsProps) { + return ( + <div + className={cx("nextra-cards mt-4 grid gap-4", "not-prose", className)} + {...props} + style={{ ...style, "--rows": num } as CSSProperties} + > + {children} + </div> + ); +} + +Cards.Card = Card; diff --git a/components/mdx/code-buttons.tsx b/components/mdx/code-buttons.tsx new file mode 100644 index 000000000..2f5188d26 --- /dev/null +++ b/components/mdx/code-buttons.tsx @@ -0,0 +1,91 @@ +"use client"; + +import { + type ComponentProps, + type MouseEvent, + useCallback, + useEffect, + useState, +} from "react"; +import { CODE_BORDER } from "./elements"; +import { CheckIcon, CopyIcon, WordWrapIcon } from "./icons"; + +const COPIED_TIMEOUT_MS = 2000; +const WORD_WRAP_ATTRIBUTE = "data-nextra-word-wrap"; + +function OutlineButton({ className, ...props }: ComponentProps<"button">) { + return ( + <button + className={[ + "cursor-pointer transition focus-visible:nextra-focus", + CODE_BORDER, + "rounded-[.375rem] p-1.5", + className, + ] + .filter(Boolean) + .join(" ")} + type="button" + {...props} + /> + ); +} + +/** Nextra `CopyToClipboard`: copies the text of the sibling `pre code`. */ +export function CopyToClipboard({ className }: { className?: string }) { + const [isCopied, setCopied] = useState(false); + + useEffect(() => { + if (!isCopied) { + return; + } + const timerId = setTimeout(() => setCopied(false), COPIED_TIMEOUT_MS); + return () => clearTimeout(timerId); + }, [isCopied]); + + const handleClick = useCallback( + async (event: MouseEvent<HTMLButtonElement>) => { + const container = event.currentTarget.parentElement?.parentElement; + const content = container?.querySelector("pre code")?.textContent || ""; + setCopied(true); + try { + await navigator.clipboard.writeText(content); + } catch { + // Clipboard unavailable (insecure context or denied permission). + } + }, + [] + ); + + const IconToUse = isCopied ? CheckIcon : CopyIcon; + return ( + <OutlineButton + className={className} + onClick={handleClick} + title="Copy code" + > + <IconToUse className="nextra-copy-icon" height="1em" /> + </OutlineButton> + ); +} + +function toggleWordWrap() { + const root = document.documentElement; + if (root.hasAttribute(WORD_WRAP_ATTRIBUTE)) { + root.removeAttribute(WORD_WRAP_ATTRIBUTE); + } else { + root.setAttribute(WORD_WRAP_ATTRIBUTE, ""); + } +} + +/** Nextra `ToggleWordWrapButton`: only shown below the `md` breakpoint. */ +export function ToggleWordWrapButton() { + return ( + <OutlineButton + className="md:hidden" + onClick={toggleWordWrap} + title="Toggle word wrap" + > + <WordWrapIcon height="1em" /> + </OutlineButton> + ); +} diff --git a/components/mdx/details.tsx b/components/mdx/details.tsx new file mode 100644 index 000000000..57ecae42b --- /dev/null +++ b/components/mdx/details.tsx @@ -0,0 +1,194 @@ +"use client"; + +import { + Children, + type ComponentProps, + cloneElement, + type Dispatch, + isValidElement, + type MouseEvent, + type ReactElement, + type ReactNode, + type SetStateAction, + useEffect, + useRef, + useState, +} from "react"; +import { cx } from "./elements"; + +const OPEN_DURATION_MS = 500; +const CLOSE_DURATION_MS = 300; + +/** Nextra's `Collapse`: animates the height of its only child. */ +function Collapse({ + className, + children, + isOpen, +}: { + className?: string; + children: ReactNode; + isOpen: boolean; +}) { + const containerRef = useRef<HTMLDivElement>(null); + const [initialOpen] = useState(isOpen); + const animationRef = useRef<number>(0); + const initialRender = useRef<boolean>(true); + + useEffect(() => { + const animation = animationRef.current; + const container = containerRef.current; + if (animation) { + clearTimeout(animation); + animationRef.current = 0; + } + if (initialRender.current || !container) { + return; + } + const child = container.children[0] as HTMLElement | undefined; + if (!child) { + return; + } + container.style.height = `${child.clientHeight}px`; + if (isOpen) { + animationRef.current = window.setTimeout(() => { + container.style.removeProperty("height"); + }, OPEN_DURATION_MS); + } else { + requestAnimationFrame(() => { + container.style.height = "0"; + }); + } + }, [isOpen]); + + useEffect(() => { + initialRender.current = false; + }, []); + + return ( + <div + className={cx( + "transform-gpu transition-all ease-in-out motion-reduce:transition-none", + isOpen ? "opacity-100" : "overflow-hidden opacity-0", + className + )} + ref={containerRef} + style={{ + ...(initialOpen ? undefined : { height: 0 }), + transitionDuration: `${isOpen ? OPEN_DURATION_MS : CLOSE_DURATION_MS}ms`, + }} + > + <div>{children}</div> + </div> + ); +} + +type ElementWithChildren = ReactElement<{ + children?: ReactNode; + onClick?: (event: MouseEvent<HTMLElement>) => void; +}>; + +function findSummary( + list: ReactNode, + setIsOpen: Dispatch<SetStateAction<boolean>> +): [ReactNode, ReactNode] { + let summary: ReactNode; + const rest = Children.map(list, (child) => { + if (summary || !isValidElement(child)) { + return child; + } + const element = child as ElementWithChildren; + if (element.type === "summary") { + summary = cloneElement(element, { + onClick(event: MouseEvent<HTMLElement>) { + if ((event.target as HTMLElement).tagName !== "A") { + event.preventDefault(); + setIsOpen((v) => !v); + } + }, + }); + return; + } + if (element.type !== Details && element.props.children) { + const [found, remaining] = findSummary(element.props.children, setIsOpen); + summary = found; + return remaining; + } + return child; + }); + return [summary, rest]; +} + +/** + * Nextra's `<details>`: a bordered box whose body slides open and closed. It + * also opens itself when the URL hash points at an element inside it. + */ +export function Details({ + children, + open, + className, + ...props +}: ComponentProps<"details">) { + const [isOpen, setIsOpen] = useState(!!open); + const [delayedOpenState, setDelayedOpenState] = useState(isOpen); + const animationRef = useRef<number>(0); + const detailsRef = useRef<HTMLDetailsElement>(null); + + useEffect(() => { + const animation = animationRef.current; + if (animation) { + clearTimeout(animation); + animationRef.current = 0; + } + if (!isOpen) { + animationRef.current = window.setTimeout( + () => setDelayedOpenState(isOpen), + CLOSE_DURATION_MS + ); + return () => { + clearTimeout(animationRef.current); + }; + } + setDelayedOpenState(true); + }, [isOpen]); + + useEffect(() => { + function openForHash() { + const hash = location.hash.replace("#", ""); + if (!hash) { + return; + } + const target = detailsRef.current?.querySelector( + `[id="${CSS.escape(decodeURIComponent(hash))}"]` + ); + if (target) { + setIsOpen(true); + } + } + openForHash(); + window.addEventListener("hashchange", openForHash); + return () => { + window.removeEventListener("hashchange", openForHash); + }; + }, []); + + const [summaryElement, restChildren] = findSummary(children, setIsOpen); + + return ( + <details + className={cx( + "not-first:mt-4 rounded-[.25rem] border border-gray-200 bg-white p-2 shadow-sm dark:border-neutral-800 dark:bg-neutral-900", + "overflow-hidden", + className + )} + ref={detailsRef} + {...props} + data-expanded={isOpen ? "" : undefined} + open={isOpen || delayedOpenState} + > + {summaryElement} + <Collapse className={cx("*:pt-2", "grid")} isOpen={isOpen}> + {restChildren} + </Collapse> + </details> + ); +} diff --git a/components/mdx/elements.tsx b/components/mdx/elements.tsx new file mode 100644 index 000000000..e7c29f6fa --- /dev/null +++ b/components/mdx/elements.tsx @@ -0,0 +1,307 @@ +import NextLink from "next/link"; +import type { ComponentProps, ReactNode } from "react"; +import { ArrowRightIcon, LinkArrowIcon, LinkIcon } from "./icons"; + +/** + * The Markdown element renderers of the Nextra docs theme (nextra-theme-docs + * 4.6 `useMDXComponents`), ported class for class. The production site was + * built on them, so headings, paragraphs, lists, links, tables, inline code, + * blockquotes and rules keep the exact sizes, margins and colours. Nextra's + * `x:`-prefixed Tailwind classes are the same utilities without the prefix; + * its named classes (`nextra-code`, `subheading-anchor`, …) are defined in the + * "content parity" block of app/global.css. + */ + +export function cx(...parts: (string | false | null | undefined)[]): string { + return parts.filter(Boolean).join(" "); +} + +const EXTERNAL_URL_RE = /^https?:\/\//; + +export const FOCUS = "focus-visible:nextra-focus"; + +/** Nextra `classes.border` from its `Pre` component. */ +export const CODE_BORDER = + "border border-gray-300 dark:border-neutral-700 contrast-more:border-gray-900 contrast-more:dark:border-gray-50"; + +/* ------------------------------------------------------------------ Links */ + +type AnchorProps = ComponentProps<"a"> & { prefetch?: boolean }; + +export function Anchor({ href = "", prefetch, ...rest }: AnchorProps) { + const props = { ...rest, className: cx(FOCUS, rest.className) }; + if (href.startsWith("#")) { + return <a href={href} {...props} />; + } + if (EXTERNAL_URL_RE.test(href)) { + const { children } = props; + return ( + <a href={href} rel="noreferrer" target="_blank" {...props}> + {children} + {typeof children === "string" ? ( + <> + {" "} + <LinkArrowIcon + className="inline shrink-0 align-baseline" + height="1em" + /> + </> + ) : null} + </a> + ); + } + return <NextLink href={href} prefetch={prefetch} {...props} />; +} + +export function Link({ className, ...props }: AnchorProps) { + return ( + <Anchor + className={cx( + "text-(--x-color-primary-600) underline decoration-from-font [text-underline-position:from-font] hover:no-underline", + className + )} + {...props} + /> + ); +} + +/* --------------------------------------------------------------- Headings */ + +type HeadingTag = "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; + +const HEADING_SIZE: Record<HeadingTag, string> = { + h1: "mt-2 text-4xl", + h2: "nextra-border mt-10 border-b pb-1 text-3xl", + h3: "mt-8 text-2xl", + h4: "mt-8 text-xl", + h5: "mt-8 text-lg", + h6: "mt-8 text-base", +}; + +function createHeading(Tag: HeadingTag) { + return function Heading({ + children, + id, + className, + ...props + }: ComponentProps<"h2">) { + const classes = + // can be added by footnotes + className === "sr-only" + ? "sr-only" + : cx( + "text-slate-900 tracking-tight dark:text-slate-100", + Tag === "h1" + ? "font-bold" + : "font-semibold target:animate-[fade-in_1.5s]", + HEADING_SIZE[Tag], + className + ); + // The "#" is drawn by `.subheading-anchor::after`, as in Nextra, so the + // heading's own text stays free of it. + const anchorProps = { + "aria-label": "Permalink for this section", + className: cx(FOCUS, "subheading-anchor"), + href: `#${id}`, + }; + return ( + <Tag className={classes} id={id} {...props}> + {children} + {id ? <a {...anchorProps} /> : null} + </Tag> + ); + }; +} + +export const H1 = createHeading("h1"); +export const H2 = createHeading("h2"); +export const H3 = createHeading("h3"); +export const H4 = createHeading("h4"); +export const H5 = createHeading("h5"); +export const H6 = createHeading("h6"); + +/* ------------------------------------------------------------ Flow content */ + +export function Paragraph(props: ComponentProps<"p">) { + return <p className="not-first:mt-[1.25em] leading-7" {...props} />; +} + +export function UnorderedList(props: ComponentProps<"ul">) { + return ( + <ul + className="ms-[1.5em] not-first:mt-[1.25em] list-disc [:is(ol,ul)_&]:my-[.75em]" + {...props} + /> + ); +} + +export function OrderedList(props: ComponentProps<"ol">) { + return ( + <ol + className="ms-6 not-first:mt-[1.25em] list-decimal [:is(ol,ul)_&]:my-[.75em]" + {...props} + /> + ); +} + +export function ListItem(props: ComponentProps<"li">) { + return <li className="my-[.5em]" {...props} />; +} + +export function Blockquote(props: ComponentProps<"blockquote">) { + return ( + <blockquote + className="not-first:mt-[1.25em] border-gray-300 border-s-2 ps-[1.5em] text-gray-700 italic dark:border-gray-700 dark:text-gray-400" + {...props} + /> + ); +} + +export function Rule(props: ComponentProps<"hr">) { + return <hr className="nextra-border my-[2em]" {...props} />; +} + +type CodeProps = ComponentProps<"code"> & { "data-language"?: string }; + +export function Code({ + children, + className, + "data-language": _language, + ...props +}: CodeProps) { + return ( + <code + className={cx( + "nextra-code", + "data-line-numbers" in props && "[counter-reset:line]", + className + )} + dir="ltr" + {...props} + > + {children} + </code> + ); +} + +/* ------------------------------------------------------------------ Tables */ + +export function Table({ className, ...props }: ComponentProps<"table">) { + return ( + <table + {...props} + className={cx( + "block overflow-x-auto", + "nextra-scrollbar not-first:mt-[1.25em] p-0", + className + )} + /> + ); +} + +export function Th({ className, ...props }: ComponentProps<"th">) { + return ( + <th + {...props} + className={cx( + "m-0 border border-gray-300 px-4 py-2 font-semibold dark:border-gray-600", + className + )} + /> + ); +} + +export function Tr({ className, ...props }: ComponentProps<"tr">) { + return ( + <tr + {...props} + className={cx( + "m-0 border-gray-300 border-t p-0 dark:border-gray-600", + "even:bg-gray-100 even:dark:bg-gray-600/20", + className + )} + /> + ); +} + +export function Td({ className, ...props }: ComponentProps<"td">) { + return ( + <td + {...props} + className={cx( + "m-0 border border-gray-300 px-4 py-2 dark:border-gray-600", + className + )} + /> + ); +} + +/** Nextra's `<Table>` component, with its `Table.Tr`, `Table.Th` and `Table.Td` parts. */ +export const TableWithParts = Object.assign(Table, { Td, Th, Tr }); + +/* ----------------------------------------------------------------- Summary */ + +export function Summary({ + children, + className, + id, + ...props +}: ComponentProps<"summary">) { + return ( + <summary + className={cx( + FOCUS, + "cursor-pointer transition-colors", + "hover:bg-gray-100 dark:hover:bg-neutral-800", + "select-none rounded-[.25rem]", + "[&::-webkit-details-marker]:hidden", + "flex items-center", + className + )} + {...props} + > + <ArrowRightIcon + className={cx( + "ms-2 me-1 shrink-0 motion-reduce:transition-none", + "transition rtl:rotate-180 [[data-expanded]>summary:first-child>&]:rotate-90" + )} + height="1em" + strokeWidth="3" + /> + <h3 className="grow hyphens-auto p-1" id={id}> + {children} + </h3> + {id ? ( + <a + aria-label="Permalink for this section" + className={cx( + "self-stretch", + "flex items-center", + "rounded-[.25rem] px-2 focus-visible:nextra-focus", + "hover:bg-gray-100 dark:hover:bg-neutral-800", + "hover:[summary:has(&)]:bg-transparent" + )} + href={`#${id}`} + > + <LinkIcon className="pointer-events-none" height="1em" /> + </a> + ) : null} + </summary> + ); +} + +/* ----------------------------------------------------------------- Wrapper */ + +/** + * Wraps the whole MDX body. `not-prose` switches off the Fumadocs typography + * preset inside it, so the Nextra element classes above are the only styling, + * as in production. The colour and font features are those of Nextra's + * `<article>`. + */ +export function Wrapper({ children }: { children?: ReactNode }) { + return ( + <div className="nextra-content not-prose w-full min-w-0 break-words text-slate-700 dark:text-slate-200"> + {children} + </div> + ); +} diff --git a/components/mdx/icons.tsx b/components/mdx/icons.tsx new file mode 100644 index 000000000..24c8c1949 --- /dev/null +++ b/components/mdx/icons.tsx @@ -0,0 +1,130 @@ +import type { SVGProps } from "react"; + +/** + * Icons copied from Nextra 4 (`nextra/icons`), so the MDX components ported in + * this folder draw exactly what the Nextra site drew. + */ +type IconProps = SVGProps<SVGSVGElement>; + +export function ArrowRightIcon(props: IconProps) { + return ( + <svg + aria-hidden="true" + fill="none" + stroke="currentColor" + strokeWidth={2} + viewBox="0 0 24 24" + {...props} + > + <path d="M9 5l7 7-7 7" strokeLinecap="round" strokeLinejoin="round" /> + </svg> + ); +} + +export function CheckIcon(props: IconProps) { + return ( + <svg aria-hidden="true" fill="currentColor" viewBox="0 0 20 20" {...props}> + <path d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" /> + </svg> + ); +} + +export function CopyIcon(props: IconProps) { + return ( + <svg + aria-hidden="true" + fill="none" + stroke="currentColor" + strokeWidth={2} + viewBox="0 0 24 24" + {...props} + > + <rect height={13} rx={2} width={13} x={9} y={9} /> + <path d="M5 15H4C2.89543 15 2 14.1046 2 13V4C2 2.89543 2.89543 2 4 2H13C14.1046 2 15 2.89543 15 4V5" /> + </svg> + ); +} + +export function LinkArrowIcon(props: IconProps) { + return ( + <svg + aria-hidden="true" + fill="none" + stroke="currentColor" + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth={1.7} + viewBox="0 0 24 24" + {...props} + > + <path d="M7 17L17 7" /> + <path d="M7 7h10v10" /> + </svg> + ); +} + +export function LinkIcon(props: IconProps) { + return ( + <svg + aria-hidden="true" + fill="none" + stroke="currentColor" + strokeWidth={1.5} + viewBox="0 0 24 24" + {...props} + > + <path + d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 00-6.364-6.364l-4.5 4.5a4.5 4.5 0 001.242 7.244" + strokeLinecap="round" + /> + </svg> + ); +} + +export function WordWrapIcon(props: IconProps) { + return ( + <svg aria-hidden="true" fill="currentColor" viewBox="0 0 24 24" {...props}> + <path d="M4 19h6v-2H4v2zM20 5H4v2h16V5zm-3 6H4v2h13.25c1.1 0 2 .9 2 2s-.9 2-2 2H15v-2l-3 3l3 3v-2h2c2.21 0 4-1.79 4-4s-1.79-4-4-4z" /> + </svg> + ); +} + +export function GitHubNoteIcon(props: IconProps) { + return ( + <svg aria-hidden="true" fill="currentColor" viewBox="0 0 16 16" {...props}> + <path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z" /> + </svg> + ); +} + +export function GitHubTipIcon(props: IconProps) { + return ( + <svg aria-hidden="true" fill="currentColor" viewBox="0 0 16 16" {...props}> + <path d="M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z" /> + </svg> + ); +} + +export function GitHubImportantIcon(props: IconProps) { + return ( + <svg aria-hidden="true" fill="currentColor" viewBox="0 0 16 16" {...props}> + <path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z" /> + </svg> + ); +} + +export function GitHubWarningIcon(props: IconProps) { + return ( + <svg aria-hidden="true" fill="currentColor" viewBox="0 0 16 16" {...props}> + <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z" /> + </svg> + ); +} + +export function GitHubCautionIcon(props: IconProps) { + return ( + <svg aria-hidden="true" fill="currentColor" viewBox="0 0 16 16" {...props}> + <path d="M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z" /> + </svg> + ); +} diff --git a/components/mdx/image-zoom.tsx b/components/mdx/image-zoom.tsx new file mode 100644 index 000000000..6a03d70fa --- /dev/null +++ b/components/mdx/image-zoom.tsx @@ -0,0 +1,73 @@ +"use client"; + +import { ImageZoom as RmizImageZoom } from "fumadocs-ui/components/image-zoom"; +import NextImage, { type ImageProps } from "next/image"; +import { type ComponentProps, useEffect, useRef, useState } from "react"; + +type ZoomableImageProps = Omit<ComponentProps<"img">, "src"> & { + src?: ComponentProps<"img">["src"] | ImageProps["src"]; + placeholder?: ImageProps["placeholder"]; +}; + +const ZOOM_MARGIN = 40; + +/** + * Nextra's `ImageZoom`, the renderer of Markdown images: click to zoom + * (react-medium-image-zoom, 40px margin), except when the image sits inside a + * link. Imported images go through `next/image`; URL strings stay a plain + * `<img>`. + */ +export function ImageZoom(props: ZoomableImageProps) { + const imgRef = useRef<HTMLImageElement>(null); + const [isInsideAnchor, setIsInsideAnchor] = useState(false); + + useEffect(() => { + setIsInsideAnchor(Boolean(imgRef.current?.closest("a"))); + }, []); + + const { src, placeholder, alt = "", ...rest } = props; + if (!src || src instanceof Blob) { + return null; + } + + const img = + typeof src === "object" ? ( + <NextImage + {...(rest as Omit<ImageProps, "src" | "alt">)} + alt={alt} + data-pagefind-index-attrs="title,alt" + placeholder={ + placeholder === "blur" && !("blurDataURL" in src) + ? "empty" + : placeholder + } + ref={imgRef} + src={src} + /> + ) : ( + // A URL image keeps the size attributes the author wrote, as in Nextra. + <img + {...rest} + alt={alt} + data-pagefind-index-attrs="title,alt" + height={rest.height} + ref={imgRef} + src={src} + width={rest.width} + /> + ); + + if (isInsideAnchor) { + return img; + } + + return ( + <RmizImageZoom + alt={alt} + rmiz={{ children: img, zoomMargin: ZOOM_MARGIN }} + src={typeof src === "object" && "default" in src ? src.default : src} + > + {img} + </RmizImageZoom> + ); +} diff --git a/components/mdx/mermaid.tsx b/components/mdx/mermaid.tsx new file mode 100644 index 000000000..5efa22a93 --- /dev/null +++ b/components/mdx/mermaid.tsx @@ -0,0 +1,96 @@ +"use client"; + +import { type RefObject, useEffect, useId, useRef, useState } from "react"; + +type MermaidProps = { + chart: string; +}; + +function useIsVisible(ref: RefObject<HTMLDivElement | null>): boolean { + const [isIntersecting, setIsIntersecting] = useState(false); + + useEffect(() => { + const element = ref.current; + if (!element) { + return; + } + const observer = new IntersectionObserver(([entry]) => { + if (entry?.isIntersecting) { + observer.disconnect(); + setIsIntersecting(true); + } + }); + observer.observe(element); + return () => { + observer.disconnect(); + }; + }, [ref]); + + return isIntersecting; +} + +/** + * The Mermaid renderer Nextra used (`@theguild/remark-mermaid`), ported: the + * diagram is drawn the first time its container scrolls into view, with the + * same Mermaid config, and redrawn when the `<html>` theme class changes. + * `remarkMdxMermaid` (source.config.ts) turns ```mermaid fences into + * `<Mermaid chart="..." />`. + */ +export function Mermaid({ chart }: MermaidProps) { + const id = useId(); + const containerRef = useRef<HTMLDivElement>(null); + const isVisible = useIsVisible(containerRef); + + useEffect(() => { + if (!isVisible) { + return; + } + const htmlElement = document.documentElement; + let cancelled = false; + + async function renderChart() { + const container = containerRef.current; + if (!container) { + return; + } + const isDarkTheme = + htmlElement.classList.contains("dark") || + htmlElement.getAttribute("data-theme") === "dark"; + const { default: mermaid } = await import("mermaid"); + try { + mermaid.initialize({ + fontFamily: "inherit", + securityLevel: "loose", + startOnLoad: false, + theme: isDarkTheme ? "dark" : "default", + themeCSS: "margin: 1.5rem auto 0;", + }); + const scratch = document.createElement("div"); + container.append(scratch); + const { svg } = await mermaid.render( + // strip invalid characters for `id` attribute + id.replaceAll(":", ""), + chart.replaceAll("\\n", "\n"), + scratch + ); + if (!cancelled) { + container.replaceChildren( + document.createRange().createContextualFragment(svg) + ); + } + } catch { + // Invalid diagram source: leave the container empty, as Nextra did. + } + } + + const observer = new MutationObserver(renderChart); + observer.observe(htmlElement, { attributes: true }); + renderChart(); + return () => { + cancelled = true; + observer.disconnect(); + }; + }, [chart, id, isVisible]); + + return <div ref={containerRef} />; +} diff --git a/components/mdx/pre.tsx b/components/mdx/pre.tsx new file mode 100644 index 000000000..fd172e656 --- /dev/null +++ b/components/mdx/pre.tsx @@ -0,0 +1,214 @@ +import { FileText, Terminal } from "lucide-react"; +import type { ComponentProps, CSSProperties, ReactNode } from "react"; +import { CopyToClipboard, ToggleWordWrapButton } from "./code-buttons"; +import { CODE_BORDER, cx } from "./elements"; + +type NextraPreProps = ComponentProps<"pre"> & { + "data-copy"?: string; + "data-filename"?: string; + "data-language"?: string; + "data-word-wrap"?: string; + icon?: ReactNode; +}; + +/** + * Nextra's `Pre` (nextra/components), ported class for class: optional + * filename bar, the hover copy button and the mobile word-wrap toggle. The + * `data-*` props are set by the Shiki transformer in source.config.ts. + */ +export function NextraPre({ + children, + className, + "data-filename": filename, + "data-copy": copy, + "data-language": _language, + "data-word-wrap": hasWordWrap, + icon, + ...props +}: NextraPreProps) { + const tokens = new Set((className ?? "").split(" ")); + const copyButton = + copy === "" ? ( + <CopyToClipboard className={filename ? "ms-auto text-sm" : ""} /> + ) : null; + + const floatingCopyButton = filename ? null : copyButton; + + return ( + <div + className="nextra-code relative not-first:mt-[1.25em]" + data-pagefind-ignore="all" + > + {filename ? ( + <div + className={cx( + "px-4 text-gray-700 text-xs dark:text-gray-200", + "bg-gray-100 dark:bg-neutral-900", + "flex h-12 items-center gap-2 rounded-t-[.375rem]", + CODE_BORDER, + "border-b-0" + )} + > + {icon} + <span className="truncate">{filename}</span> + {copyButton} + </div> + ) : null} + <pre + className={cx( + "group", + "focus-visible:nextra-focus", + "overflow-x-auto subpixel-antialiased", + // The production wrapper's own classes came later in the cascade + // than Nextra's, so they replace these defaults when present. + tokens.has("text-sm") ? undefined : "text-[.9em]", + "bg-white py-4", + tokens.has("dark:bg-gray-950") ? undefined : "dark:bg-black", + "ring-1 ring-gray-300 ring-inset dark:ring-neutral-700", + "contrast-more:ring-gray-900 contrast-more:dark:ring-gray-50", + "contrast-more:contrast-150", + filename ? "rounded-b-[.375rem]" : "rounded-[.375rem]", + "not-prose", + className + )} + {...props} + > + <div + className={cx( + "group-hover:opacity-100", + "group-focus:opacity-100", + "opacity-0 transition focus-within:opacity-100", + "absolute right-4 flex gap-1", + filename ? "top-14" : "top-2" + )} + > + {hasWordWrap === "" ? <ToggleWordWrapButton /> : null} + {floatingCopyButton} + </div> + {children} + </pre> + </div> + ); +} + +const languageDisplayNames: Record<string, string> = { + c: "C", + cpp: "C++", + css: "CSS", + go: "Go", + html: "HTML", + java: "Java", + javascript: "JavaScript", + js: "JavaScript", + json: "JSON", + jsx: "JSX", + markdown: "Markdown", + md: "Markdown", + php: "PHP", + py: "Python", + python: "Python", + rb: "Ruby", + ruby: "Ruby", + rust: "Rust", + sass: "Sass", + scss: "SCSS", + sql: "SQL", + toml: "TOML", + ts: "TypeScript", + tsx: "TSX", + typescript: "TypeScript", + xml: "XML", + yaml: "YAML", + yml: "YAML", +}; + +const TERMINAL_LANGUAGES = new Set([ + "bash", + "shell", + "sh", + "zsh", + "fish", + "powershell", + "ps1", + "cmd", +]); + +const getLanguageDisplayName = (lang: string): string => + languageDisplayNames[lang.toLowerCase()] || lang.toUpperCase(); + +const TERMINAL_PRE_STYLE: CSSProperties = { + background: "transparent", + borderRadius: 0, + margin: 0, +}; +const CODE_PRE_STYLE: CSSProperties = { borderRadius: 0, margin: 0 }; + +/** + * The production `custom-pre.tsx`: shell languages get a "Terminal" header + * with the three window dots, every other language a header with its name, + * both wrapped around Nextra's `Pre`. + */ +export function Pre({ + children, + className, + "data-language": dataLanguage, + ...props +}: NextraPreProps) { + const language = dataLanguage || className?.replace("language-", "") || ""; + + if (language && language.trim() !== "") { + if (TERMINAL_LANGUAGES.has(language.toLowerCase())) { + return ( + <div className="my-4 overflow-hidden rounded-lg border border-gray-300 bg-gray-100 dark:border-gray-700 dark:bg-gray-900"> + <div className="flex items-center justify-between bg-gray-200 px-4 py-2 text-gray-700 dark:bg-gray-800 dark:text-gray-300"> + <div className="flex items-center gap-2"> + <Terminal className="h-4 w-4" /> + <span className="font-medium text-sm">Terminal</span> + </div> + <div className="flex gap-2"> + <div className="h-3 w-3 rounded-full bg-red-500" /> + <div className="h-3 w-3 rounded-full bg-yellow-500" /> + <div className="h-3 w-3 rounded-full bg-green-500" /> + </div> + </div> + <div className="overflow-x-auto"> + <NextraPre + className={`text-gray-800 text-sm dark:text-gray-100 ${className || ""}`} + style={TERMINAL_PRE_STYLE} + {...props} + > + {children} + </NextraPre> + </div> + </div> + ); + } + return ( + <div className="my-4 overflow-hidden rounded border border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-950"> + <div className="flex items-center justify-between border-gray-200 border-b bg-gray-50 px-4 py-2 dark:border-gray-800 dark:bg-gray-900"> + <div className="flex items-center gap-2"> + <FileText className="h-4 w-4 text-gray-600 dark:text-gray-400" /> + <span className="font-medium text-gray-700 text-sm dark:text-gray-300"> + {getLanguageDisplayName(language)} + </span> + </div> + </div> + <div className="overflow-x-auto"> + <NextraPre + className={`bg-white text-sm dark:bg-gray-950 ${className || ""}`} + style={CODE_PRE_STYLE} + {...props} + > + {children} + </NextraPre> + </div> + </div> + ); + } + + return ( + <NextraPre className={className} data-language={dataLanguage} {...props}> + {children} + </NextraPre> + ); +} diff --git a/components/mdx/steps.tsx b/components/mdx/steps.tsx new file mode 100644 index 000000000..b32b7cdfe --- /dev/null +++ b/components/mdx/steps.tsx @@ -0,0 +1,34 @@ +import { type ComponentProps, type CSSProperties, useId } from "react"; +import { cx } from "./elements"; + +/** + * Nextra's `<Steps>` (nextra/components): a left rule, with every `h3` inside + * numbered by a CSS counter (`.nextra-steps` in app/global.css). No `<Step>` + * wrappers are needed, exactly as in the Nextra-authored MDX. + */ +export function Steps({ + children, + className, + style, + ...props +}: ComponentProps<"div">) { + const id = useId().replaceAll(":", ""); + return ( + <div + className={cx( + "nextra-steps ms-4 mb-12 border-gray-200 border-s ps-6", + "dark:border-neutral-800", + className + )} + style={{ ...style, "--counter-id": id } as CSSProperties} + {...props} + > + {children} + </div> + ); +} + +/** Kept for MDX that wraps a step explicitly; renders its children as-is. */ +export function Step({ children }: ComponentProps<"div">) { + return <>{children}</>; +} diff --git a/components/mdx/tabs.client.tsx b/components/mdx/tabs.client.tsx new file mode 100644 index 000000000..f3cc5b92e --- /dev/null +++ b/components/mdx/tabs.client.tsx @@ -0,0 +1,321 @@ +"use client"; + +import { + Children, + type ComponentProps, + createContext, + isValidElement, + type KeyboardEvent, + type MouseEvent, + type ReactNode, + useCallback, + useContext, + useEffect, + useId, + useMemo, + useRef, + useState, +} from "react"; +import { cx } from "./elements"; + +type TabItem = ReactNode | { label: ReactNode; disabled?: boolean }; + +function isTabObjectItem( + item: TabItem +): item is { label: ReactNode; disabled?: boolean } { + return !!item && typeof item === "object" && "label" in item; +} + +export type TabsProps = { + children?: ReactNode; + className?: string; + defaultIndex?: number; + items: TabItem[]; + onChange?: (index: number) => void; + selectedIndex?: number; + /** Shares and remembers the selected tab (localStorage) across groups. */ + storageKey?: string; + tabClassName?: string; +}; + +type TabsContextValue = { + baseId: string; + selected: number; +}; + +const TabsContext = createContext<TabsContextValue | null>(null); +const TabIndexContext = createContext(0); + +function clampIndex(index: number, count: number): number { + if (!Number.isFinite(index) || index < 0) { + return 0; + } + return Math.min(index, Math.max(count - 1, 0)); +} + +/** The enabled tab reached from `from` by `key`, as Headless UI resolves it. */ +function targetIndex( + key: string, + from: number, + disabled: boolean[] +): number | undefined { + const count = disabled.length; + const enabled = disabled.flatMap((isDisabled, i) => (isDisabled ? [] : [i])); + if (key === "Home" || key === "PageUp") { + return enabled[0]; + } + if (key === "End" || key === "PageDown") { + return enabled.at(-1); + } + if (key !== "ArrowRight" && key !== "ArrowLeft") { + return; + } + const step = key === "ArrowRight" ? 1 : -1; + for (let offset = 1; offset <= count; offset += 1) { + const next = (((from + step * offset) % count) + count) % count; + if (!disabled[next]) { + return next; + } + } +} + +function readStoredIndex(storageKey: string): number { + let stored: string | null = null; + try { + stored = localStorage.getItem(storageKey); + } catch { + // localStorage unavailable (private mode, blocked storage). + } + const index = Number(stored); + return Number.isNaN(index) ? 0 : index; +} + +/** Index of the panel that contains the element the URL hash points at. */ +function panelIndexForHash(panels: HTMLElement, hash: string): number { + let target: Element | null = null; + try { + target = panels.querySelector( + `[role=tabpanel]:has([id="${CSS.escape(decodeURIComponent(hash))}"])` + ); + } catch { + return -1; + } + return target ? Array.from(panels.children).indexOf(target) : -1; +} + +/** + * Nextra's `<Tabs items={[...]} storageKey="...">`, ported: underline tabs, + * every panel rendered into the HTML (inactive ones `hidden`), the selected + * index stored in `localStorage[storageKey]` and synced between groups through + * `storage` events, and a hash that points inside a panel selects that panel. + * Keyboard handling follows Headless UI's `TabGroup`, which Nextra used. + */ +export function Tabs({ + items, + children, + storageKey, + defaultIndex = 0, + selectedIndex: controlledIndex, + onChange, + className, + tabClassName, +}: TabsProps) { + const [selectedIndex, setSelectedIndex] = useState(defaultIndex); + const tabPanelsRef = useRef<HTMLDivElement>(null); + const baseId = useId(); + + useEffect(() => { + if (controlledIndex !== undefined) { + setSelectedIndex(controlledIndex); + } + }, [controlledIndex]); + + useEffect(() => { + function selectPanelForHash() { + const hash = location.hash.replace("#", ""); + const panels = tabPanelsRef.current; + if (!(hash && panels)) { + return; + } + const index = panelIndexForHash(panels, hash); + if (index === -1) { + return; + } + setSelectedIndex(index); + // Re-apply the hash so the browser scrolls once the panel is visible. + location.hash = ""; + requestAnimationFrame(() => { + location.hash = `#${hash}`; + }); + } + selectPanelForHash(); + window.addEventListener("hashchange", selectPanelForHash); + return () => { + window.removeEventListener("hashchange", selectPanelForHash); + }; + }, []); + + useEffect(() => { + if (!storageKey) { + return; + } + function onStorage(event: StorageEvent) { + if (event.key === storageKey) { + setSelectedIndex(Number(event.newValue)); + } + } + setSelectedIndex(readStoredIndex(storageKey)); + window.addEventListener("storage", onStorage); + return () => { + window.removeEventListener("storage", onStorage); + }; + }, [storageKey]); + + const handleChange = useCallback( + (index: number) => { + if (storageKey) { + const newValue = String(index); + try { + localStorage.setItem(storageKey, newValue); + } catch { + // localStorage unavailable: still sync the groups on this page. + } + window.dispatchEvent( + new StorageEvent("storage", { key: storageKey, newValue }) + ); + return; + } + setSelectedIndex(index); + onChange?.(index); + }, + [storageKey, onChange] + ); + + const disabledTabs = useMemo( + () => items.map((item) => isTabObjectItem(item) && !!item.disabled), + [items] + ); + + const onTabClick = useCallback( + (event: MouseEvent<HTMLButtonElement>) => { + handleChange(Number(event.currentTarget.dataset.index)); + }, + [handleChange] + ); + + const onTabKeyDown = useCallback( + (event: KeyboardEvent<HTMLButtonElement>) => { + const from = Number(event.currentTarget.dataset.index); + const next = targetIndex(event.key, from, disabledTabs); + if (next === undefined) { + return; + } + event.preventDefault(); + event.stopPropagation(); + const tabs = + event.currentTarget.parentElement?.querySelectorAll<HTMLElement>( + "[role=tab]" + ); + tabs?.[next]?.focus(); + handleChange(next); + }, + [disabledTabs, handleChange] + ); + + const selected = clampIndex(selectedIndex, items.length); + const context = useMemo(() => ({ baseId, selected }), [baseId, selected]); + const tabs = items.map((item, index) => ({ + disabled: disabledTabs[index] ?? false, + id: `${baseId}-tab-${index}`, + index, + isSelected: index === selected, + label: isTabObjectItem(item) ? item.label : item, + })); + const panels = Children.toArray(children) + .filter(isValidElement) + .map((panel, index) => ({ + id: `${baseId}-panel-${index}`, + index, + panel, + })); + + return ( + <TabsContext.Provider value={context}> + <div + aria-orientation="horizontal" + className={cx( + "nextra-scrollbar overflow-x-auto overflow-y-hidden overscroll-x-contain", + "mt-4 flex w-full gap-2 border-gray-200 border-b pb-px dark:border-neutral-800", + "focus-visible:nextra-focus", + className + )} + role="tablist" + > + {tabs.map(({ disabled, id, index, isSelected, label }) => ( + <button + aria-controls={`${baseId}-panel-${index}`} + aria-selected={isSelected} + className={cx( + "focus-visible:nextra-focus focus-visible:ring-inset", + "cursor-pointer whitespace-nowrap", + "rounded-t-[.25rem] p-2 font-medium leading-5 transition-colors", + "-mb-0.5 select-none border-b-2", + isSelected && + "border-current text-(--x-color-primary-600) outline-none", + !isSelected && + disabled && + "pointer-events-none border-transparent text-gray-400 dark:text-neutral-600", + !(isSelected || disabled) && + "border-transparent text-gray-600 hover:border-gray-200 hover:text-black dark:text-gray-200 dark:hover:border-neutral-800 dark:hover:text-white", + tabClassName + )} + data-index={index} + data-selected={isSelected ? "" : undefined} + disabled={disabled} + id={id} + key={id} + onClick={onTabClick} + onKeyDown={onTabKeyDown} + role="tab" + tabIndex={isSelected ? 0 : -1} + type="button" + > + {label} + </button> + ))} + </div> + <div ref={tabPanelsRef}> + {panels.map(({ panel, index, id }) => ( + <TabIndexContext.Provider key={id} value={index}> + {panel} + </TabIndexContext.Provider> + ))} + </div> + </TabsContext.Provider> + ); +} + +/** `<Tabs.Tab>`: one panel. Stays in the DOM when it is not selected. */ +export function Tab({ children, className, ...props }: ComponentProps<"div">) { + const context = useContext(TabsContext); + const index = useContext(TabIndexContext); + const isSelected = context ? context.selected === index : true; + return ( + <div + aria-labelledby={context ? `${context.baseId}-tab-${index}` : undefined} + className={cx( + "mt-[1.25em] rounded-[.25rem] focus-visible:nextra-focus", + className + )} + data-selected={isSelected ? "" : undefined} + hidden={!isSelected} + id={context ? `${context.baseId}-panel-${index}` : undefined} + role="tabpanel" + style={isSelected ? undefined : { display: "none" }} + tabIndex={isSelected ? 0 : -1} + {...props} + > + {children} + </div> + ); +} diff --git a/components/mdx/tabs.tsx b/components/mdx/tabs.tsx new file mode 100644 index 000000000..a88b8eca8 --- /dev/null +++ b/components/mdx/tabs.tsx @@ -0,0 +1,11 @@ +import { Tabs as ClientTabs, Tab, type TabsProps } from "./tabs.client"; + +/** + * Server-side entry for `<Tabs>` / `<Tabs.Tab>`. MDX is rendered on the server + * and cannot read a property (`.Tab`) off a client component, so the compound + * component is assembled here, as Nextra does. + */ +export const Tabs = Object.assign( + (props: TabsProps) => <ClientTabs {...props} />, + { Tab } +); diff --git a/app/_components/nav-bar-button.tsx b/components/nav-bar-button.tsx similarity index 100% rename from app/_components/nav-bar-button.tsx rename to components/nav-bar-button.tsx diff --git a/components/nav-links.tsx b/components/nav-links.tsx new file mode 100644 index 000000000..5950d876e --- /dev/null +++ b/components/nav-links.tsx @@ -0,0 +1,21 @@ +import Link from "next/link"; +import { SignupLink } from "@/components/analytics"; +import { getDashboardUrl } from "@/components/dashboard-link"; +import { NavBarButton } from "@/components/nav-bar-button"; + +/** "Sign Up" / "Dashboard" buttons at the end of the top navbar. */ +export function NavLinks() { + return ( + <> + <SignupLink linkLocation="docs:navbar" utmMedium="navbar"> + <NavBarButton + hideOnPath={["/build/create-tools/add-tools-to-arcade-catalog"]} + text="Sign Up" + /> + </SignupLink> + <Link href={getDashboardUrl("playground/chat")}> + <NavBarButton text="Dashboard" variant="outline" /> + </Link> + </> + ); +} diff --git a/app/_components/note.tsx b/components/note.tsx similarity index 100% rename from app/_components/note.tsx rename to components/note.tsx diff --git a/components/page-actions.tsx b/components/page-actions.tsx new file mode 100644 index 000000000..dd09c0220 --- /dev/null +++ b/components/page-actions.tsx @@ -0,0 +1,243 @@ +"use client"; + +import { cn } from "@arcadeai/design-system/lib/utils"; +import { + type ReactNode, + useCallback, + useEffect, + useRef, + useState, +} from "react"; +import { + ArrowRightIcon, + ChatGPTIcon, + ClaudeIcon, + CopyIcon, + LinkArrowIcon, +} from "@/components/chrome/icons"; +import { Select } from "@/components/chrome/select"; + +type PageActionsProps = { + /** Kept for callers that pass it; the menu has no GitHub entry. */ + githubUrl?: string; + /** URL that returns the page as Markdown (app/llms.mdx or /api/toolkit-data). */ + markdownUrl: string; +}; + +type CopyState = "idle" | "copying" | "copied" | "failed"; + +const COPY_FEEDBACK_DELAY_MS = 2000; + +const LABELS: Record<CopyState, string> = { + copied: "Copied", + copying: "Copying…", + failed: "Failed to copy", + idle: "Copy page", +}; + +function StateIcon({ state }: { state: CopyState }) { + if (state === "copying") { + return ( + <svg + aria-hidden="true" + className="animate-spin" + fill="none" + stroke="currentColor" + strokeWidth="2" + viewBox="0 0 24 24" + width="16" + > + <circle cx="12" cy="12" r="10" strokeOpacity="0.25" /> + <path d="M12 2a10 10 0 0 1 10 10" strokeLinecap="round" /> + </svg> + ); + } + if (state === "copied") { + return ( + <svg + aria-hidden="true" + fill="none" + stroke="var(--color-green-600)" + strokeWidth="2.5" + viewBox="0 0 24 24" + width="16" + > + <path + d="M20 6L9 17l-5-5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + ); + } + if (state === "failed") { + return ( + <svg + aria-hidden="true" + fill="none" + stroke="var(--color-red-600)" + strokeWidth="2" + viewBox="0 0 24 24" + width="16" + > + <circle cx="12" cy="12" r="10" /> + <line strokeLinecap="round" x1="15" x2="9" y1="9" y2="15" /> + <line strokeLinecap="round" x1="9" x2="15" y1="9" y2="15" /> + </svg> + ); + } + return <CopyIcon width="16" />; +} + +function stateColor(state: CopyState): string | undefined { + if (state === "copied") { + return "var(--color-green-600)"; + } + if (state === "failed") { + return "var(--color-red-600)"; + } +} + +function Item({ + description, + icon, + isExternal, + title, +}: { + description: string; + icon: ReactNode; + isExternal?: boolean; + title: string; +}) { + return ( + <div className="flex items-center gap-3"> + {icon} + <div className="flex flex-col"> + <span className="flex gap-1 font-medium"> + {title} + {isExternal ? <LinkArrowIcon height="1em" /> : null} + </span> + <span className="text-xs">{description}</span> + </div> + </div> + ); +} + +const OPTIONS = [ + { + id: "copy", + name: ( + <Item + description="Copy page as Markdown for LLMs" + icon={<CopyIcon width="16" />} + title="Copy page" + /> + ), + }, + { + id: "chatgpt", + name: ( + <Item + description="Ask questions about this page" + icon={<ChatGPTIcon width="16" />} + isExternal + title="Open in ChatGPT" + /> + ), + }, + { + id: "claude", + name: ( + <Item + description="Ask questions about this page" + icon={<ClaudeIcon width="16" />} + isExternal + title="Open in Claude" + /> + ), + }, +]; + +/** + * "Copy page" split button shown at the top right of a page. The main button + * copies the page as Markdown; the menu also offers to open the page in + * ChatGPT or Claude. + */ +export function PageActions({ markdownUrl }: PageActionsProps) { + const [state, setState] = useState<CopyState>("idle"); + const isFetchingRef = useRef<boolean>(false); + const feedbackTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined); + + useEffect(() => () => clearTimeout(feedbackTimerRef.current), []); + + const handleCopy = useCallback(async () => { + if (isFetchingRef.current) { + return; + } + clearTimeout(feedbackTimerRef.current); + isFetchingRef.current = true; + setState("copying"); + + let success = false; + try { + const response = await fetch(markdownUrl, { + headers: { Accept: "text/markdown" }, + }); + if (!response.ok) { + throw new Error(`Failed to fetch markdown: ${response.status}`); + } + await navigator.clipboard.writeText(await response.text()); + success = true; + } catch { + success = false; + } + + isFetchingRef.current = false; + setState(success ? "copied" : "failed"); + feedbackTimerRef.current = setTimeout(() => { + setState("idle"); + }, COPY_FEEDBACK_DELAY_MS); + }, [markdownUrl]); + + const handleChange = useCallback( + (value: string) => { + if (value === "copy") { + handleCopy(); + return; + } + const url = + value === "chatgpt" + ? "chatgpt.com/?hints=search&prompt" + : "claude.ai/new?q"; + const query = `Read from ${window.location.href} so I can ask questions about it.`; + window.open(`https://${url}=${encodeURIComponent(query)}`, "_blank"); + }, + [handleCopy] + ); + + return ( + <div className="nextra-border float-end inline-flex items-stretch overflow-hidden rounded-[6px] border"> + <button + className={cn( + "cursor-pointer transition focus-visible:nextra-focus", + "flex items-center gap-2 ps-2 pe-1 font-medium text-sm", + "hover:bg-gray-200 hover:text-gray-900 dark:hover:bg-primary-100/5 dark:hover:text-gray-50" + )} + onClick={handleCopy} + style={{ color: stateColor(state) }} + type="button" + > + <StateIcon state={state} /> + {LABELS[state]} + </button> + <Select + anchor="bottom end" + className="rounded-none" + onChange={handleChange} + options={OPTIONS} + selectedOptionName={<ArrowRightIcon className="rotate-90" width="12" />} + value="" + /> + </div> + ); +} diff --git a/app/en/get-started/mcp-clients/_meta.tsx b/components/pages/get-started/mcp-clients/client-titles.ts similarity index 70% rename from app/en/get-started/mcp-clients/_meta.tsx rename to components/pages/get-started/mcp-clients/client-titles.ts index 974c617d1..912d98d37 100644 --- a/app/en/get-started/mcp-clients/_meta.tsx +++ b/components/pages/get-started/mcp-clients/client-titles.ts @@ -1,34 +1,26 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "*": { - theme: { - breadcrumb: true, - toc: true, - copyPage: true, - }, - }, - index: { - title: "Overview", - }, - cursor: { - title: "Cursor", - }, - "claude-desktop": { - title: "Claude Desktop", - }, +// Display titles for the MCP client pages, keyed by page slug. +const meta: Record<string, { title?: string }> = { "claude-code": { title: "Claude Code", }, - "visual-studio-code": { - title: "Visual Studio Code", + "claude-desktop": { + title: "Claude Desktop", }, "copilot-studio": { title: "Microsoft Copilot Studio", }, + cursor: { + title: "Cursor", + }, "github-copilot": { title: "GitHub Copilot", }, + index: { + title: "Overview", + }, + "visual-studio-code": { + title: "Visual Studio Code", + }, }; export default meta; diff --git a/app/en/get-started/mcp-clients/mcp-client-grid.tsx b/components/pages/get-started/mcp-clients/mcp-client-grid.tsx similarity index 97% rename from app/en/get-started/mcp-clients/mcp-client-grid.tsx rename to components/pages/get-started/mcp-clients/mcp-client-grid.tsx index 1ffbb39e2..72e149055 100644 --- a/app/en/get-started/mcp-clients/mcp-client-grid.tsx +++ b/components/pages/get-started/mcp-clients/mcp-client-grid.tsx @@ -1,6 +1,6 @@ "use client"; import Link from "next/link"; -import meta from "./_meta"; +import meta from "./client-titles"; // MCP Client data with logos and descriptions const mcpClients = [ @@ -56,7 +56,7 @@ export function MCPClientGrid() { return ( <Link - href={`/guides/tool-calling/mcp-clients/${client.key}`} + href={`/en/get-started/mcp-clients/${client.key}`} key={client.key} > <div className="flex h-full flex-col gap-1.5 rounded-xl border border-blue-600/20 bg-blue-600/[0.02] py-3 text-card-foreground shadow-sm backdrop-blur-sm transition-all duration-300 hover:border-primary hover:bg-blue-600/[0.03] hover:shadow-lg dark:bg-gray-900/80"> diff --git a/app/en/home/landing-page.tsx b/components/pages/home/landing-page.tsx similarity index 99% rename from app/en/home/landing-page.tsx rename to components/pages/home/landing-page.tsx index 0ffaac342..5a6b914d5 100644 --- a/app/en/home/landing-page.tsx +++ b/components/pages/home/landing-page.tsx @@ -20,8 +20,8 @@ import { motion } from "motion/react"; import Image from "next/image"; import Link from "next/link"; import posthog from "posthog-js"; -import { QuickStartCard } from "../../_components/quick-start-card"; -import { SampleAppCard } from "../../_components/sample-app-card"; +import { QuickStartCard } from "@/components/quick-start-card"; +import { SampleAppCard } from "@/components/sample-app-card"; const ANIMATION_DURATION = 0.5; const ANIMATION_DELAYS = { diff --git a/app/en/resources/integrations/components/coming-soon-modal.tsx b/components/pages/integrations/coming-soon-modal.tsx similarity index 98% rename from app/en/resources/integrations/components/coming-soon-modal.tsx rename to components/pages/integrations/coming-soon-modal.tsx index 21549e184..1795e39ae 100644 --- a/app/en/resources/integrations/components/coming-soon-modal.tsx +++ b/components/pages/integrations/coming-soon-modal.tsx @@ -11,7 +11,7 @@ import { import posthog from "posthog-js"; import type React from "react"; import { useState } from "react"; -import { useComingSoon } from "@/app/_components/coming-soon-context"; +import { useComingSoon } from "@/components/coming-soon-context"; type ComingSoonModalProps = { isOpen: boolean; diff --git a/app/en/resources/integrations/components/filter-params.ts b/components/pages/integrations/filter-params.ts similarity index 100% rename from app/en/resources/integrations/components/filter-params.ts rename to components/pages/integrations/filter-params.ts diff --git a/app/en/resources/integrations/components/filters-bar.tsx b/components/pages/integrations/filters-bar.tsx similarity index 100% rename from app/en/resources/integrations/components/filters-bar.tsx rename to components/pages/integrations/filters-bar.tsx diff --git a/app/en/resources/integrations/components/tool-card.tsx b/components/pages/integrations/tool-card.tsx similarity index 100% rename from app/en/resources/integrations/components/tool-card.tsx rename to components/pages/integrations/tool-card.tsx diff --git a/app/en/resources/integrations/toolkit-page-theme.ts b/components/pages/integrations/toolkit-page-theme.ts similarity index 100% rename from app/en/resources/integrations/toolkit-page-theme.ts rename to components/pages/integrations/toolkit-page-theme.ts diff --git a/app/en/resources/integrations/components/toolkit-utils.ts b/components/pages/integrations/toolkit-utils.ts similarity index 100% rename from app/en/resources/integrations/components/toolkit-utils.ts rename to components/pages/integrations/toolkit-utils.ts diff --git a/app/en/resources/integrations/components/toolkits-client.tsx b/components/pages/integrations/toolkits-client.tsx similarity index 98% rename from app/en/resources/integrations/components/toolkits-client.tsx rename to components/pages/integrations/toolkits-client.tsx index 942205df9..22bfbdd57 100644 --- a/app/en/resources/integrations/components/toolkits-client.tsx +++ b/components/pages/integrations/toolkits-client.tsx @@ -4,11 +4,11 @@ import { Generic as GenericIcon } from "@arcadeai/design-system/components/ui/at import { cn } from "@arcadeai/design-system/lib/utils"; import { Code2, MessageSquarePlus, Search } from "lucide-react"; import Link from "next/link"; -import { ComingSoonProvider } from "@/app/_components/coming-soon-context"; +import { ComingSoonProvider } from "@/components/coming-soon-context"; import { type ResolvedIndexToolkit, toIntegrationLink, -} from "@/app/_lib/integration-index"; +} from "@/lib/integration-index"; import { FiltersBar } from "./filters-bar"; import { ToolCard } from "./tool-card"; import { TYPE_CONFIG, TYPE_DESCRIPTIONS } from "./type-config"; diff --git a/app/en/resources/integrations/components/toolkits.tsx b/components/pages/integrations/toolkits.tsx similarity index 77% rename from app/en/resources/integrations/components/toolkits.tsx rename to components/pages/integrations/toolkits.tsx index 62656b0cd..666d32de6 100644 --- a/app/en/resources/integrations/components/toolkits.tsx +++ b/components/pages/integrations/toolkits.tsx @@ -1,7 +1,7 @@ import { Suspense } from "react"; -import { getToolkitsWithDocsLinks } from "@/app/_lib/integration-catalog"; -import { resolveIndexToolkits } from "@/app/_lib/integration-index"; -import { listValidIntegrationLinks } from "@/app/_lib/toolkit-static-params"; +import { getToolkitsWithDocsLinks } from "@/lib/integration-catalog"; +import { resolveIndexToolkits } from "@/lib/integration-index"; +import { listValidIntegrationLinks } from "@/lib/toolkit-static-params"; import ToolkitsClient from "./toolkits-client"; export default async function Toolkits() { diff --git a/app/en/resources/integrations/components/type-config.tsx b/components/pages/integrations/type-config.tsx similarity index 100% rename from app/en/resources/integrations/components/type-config.tsx rename to components/pages/integrations/type-config.tsx diff --git a/app/en/resources/integrations/components/use-filters.ts b/components/pages/integrations/use-filters.ts similarity index 100% rename from app/en/resources/integrations/components/use-filters.ts rename to components/pages/integrations/use-filters.ts diff --git a/app/en/resources/integrations/components/use-toolkit-filters.ts b/components/pages/integrations/use-toolkit-filters.ts similarity index 100% rename from app/en/resources/integrations/components/use-toolkit-filters.ts rename to components/pages/integrations/use-toolkit-filters.ts diff --git a/app/en/references/api/swaggerHacks.css b/components/pages/references/api/swaggerHacks.css similarity index 100% rename from app/en/references/api/swaggerHacks.css rename to components/pages/references/api/swaggerHacks.css diff --git a/app/en/references/contextual-access-webhook-api/swaggerHacks.css b/components/pages/references/contextual-access-webhook-api/swaggerHacks.css similarity index 100% rename from app/en/references/contextual-access-webhook-api/swaggerHacks.css rename to components/pages/references/contextual-access-webhook-api/swaggerHacks.css diff --git a/app/en/resources/contact-us/contact-cards.tsx b/components/pages/resources/contact-us/contact-cards.tsx similarity index 93% rename from app/en/resources/contact-us/contact-cards.tsx rename to components/pages/resources/contact-us/contact-cards.tsx index 1c4cd5ad1..adc4138fe 100644 --- a/app/en/resources/contact-us/contact-cards.tsx +++ b/components/pages/resources/contact-us/contact-cards.tsx @@ -20,9 +20,9 @@ import { Users, } from "lucide-react"; import posthog from "posthog-js"; -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { useForm } from "react-hook-form"; -import { QuickStartCard } from "../../../_components/quick-start-card"; +import { QuickStartCard } from "@/components/quick-start-card"; const WEBHOOK_URL = process.env.NEXT_PUBLIC_ATTIO_WEBHOOK_URL; @@ -233,12 +233,12 @@ function ContactSalesForm({ onSuccess }: { onSuccess: () => void }) { {...register("website")} /> </div> - {submitError && ( + {submitError ? ( <div className="flex items-center gap-3 rounded-lg bg-red-500/10 p-3 text-white"> <AlertOctagon className="h-5 w-5 shrink-0" /> <span className="text-sm">{submitError}</span> </div> - )} + ) : null} <Button className="w-full" disabled={isSubmitting} type="submit"> {isSubmitting ? "Please wait..." : "Get in touch"} </Button> @@ -267,23 +267,34 @@ export function ContactCards() { // Assemble the support mailto only after mount so the SSR/crawled markup // shows a plain contact-page link — Cloudflare's email obfuscation then has // nothing to rewrite into a broken /cdn-cgi/l/email-protection link. Mirrors - // <ContactEmail> (see app/_components/contact-email.tsx). + // <ContactEmail> (see components/contact-email.tsx). const [supportHref, setSupportHref] = useState("/en/resources/contact-us"); useEffect(() => { setSupportHref("mailto:support@arcade.dev"); }, []); - const handleContactSalesClick = () => { + const handleContactSalesClick = useCallback(() => { posthog.capture("Contact sales modal opened", { source: "contact_us_page", }); setIsSalesModalOpen(true); - }; + }, []); - const handleClose = () => { + const handleClose = useCallback(() => { setIsSalesModalOpen(false); setIsSubmitted(false); - }; + }, []); + + const handleOpenChange = useCallback( + (open: boolean) => { + if (!open) { + handleClose(); + } + }, + [handleClose] + ); + + const handleSubmitted = useCallback(() => setIsSubmitted(true), []); return ( <> @@ -325,10 +336,7 @@ export function ContactCards() { title="System Status" /> </div> - <Dialog - onOpenChange={(open) => !open && handleClose()} - open={isSalesModalOpen} - > + <Dialog onOpenChange={handleOpenChange} open={isSalesModalOpen}> <DialogContent className="border-gray-800 bg-gray-900 sm:max-w-[500px]"> <DialogHeader> <DialogTitle className="text-gray-100">Contact Sales</DialogTitle> @@ -336,7 +344,7 @@ export function ContactCards() { {isSubmitted ? ( <SuccessMessage onClose={handleClose} /> ) : ( - <ContactSalesForm onSuccess={() => setIsSubmitted(true)} /> + <ContactSalesForm onSuccess={handleSubmitted} /> )} </DialogContent> </Dialog> diff --git a/app/en/resources/integrations/tool-feedback/take-survey.tsx b/components/pages/resources/integrations/tool-feedback/take-survey.tsx similarity index 100% rename from app/en/resources/integrations/tool-feedback/take-survey.tsx rename to components/pages/resources/integrations/tool-feedback/take-survey.tsx diff --git a/app/en/resources/registry-early-access/take-survey.tsx b/components/pages/resources/registry-early-access/take-survey.tsx similarity index 100% rename from app/en/resources/registry-early-access/take-survey.tsx rename to components/pages/resources/registry-early-access/take-survey.tsx diff --git a/app/_components/placeholder-replacer.tsx b/components/placeholder-replacer.tsx similarity index 96% rename from app/_components/placeholder-replacer.tsx rename to components/placeholder-replacer.tsx index e7dbf6f48..6cc5e64c1 100644 --- a/app/_components/placeholder-replacer.tsx +++ b/components/placeholder-replacer.tsx @@ -1,7 +1,7 @@ "use client"; import { useEffect } from "react"; -import { useOrySession } from "@/app/_lib/ory-session-context"; -import { getCookie } from "@/app/_lib/utils"; +import { useOrySession } from "@/lib/ory-session-context"; +import { getCookie } from "@/lib/utils"; export function PlaceholderReplacer() { const { email, loading } = useOrySession(); diff --git a/app/_components/platform-card.tsx b/components/platform-card.tsx similarity index 100% rename from app/_components/platform-card.tsx rename to components/platform-card.tsx diff --git a/app/_components/quick-start-card.tsx b/components/quick-start-card.tsx similarity index 100% rename from app/_components/quick-start-card.tsx rename to components/quick-start-card.tsx diff --git a/components/root-provider.tsx b/components/root-provider.tsx new file mode 100644 index 000000000..bc6f9ae26 --- /dev/null +++ b/components/root-provider.tsx @@ -0,0 +1,20 @@ +"use client"; + +import { RootProvider } from "fumadocs-ui/provider/next"; +import type { ReactNode } from "react"; + +/** + * Fumadocs root provider with the site defaults: dark theme by default and no + * theme hotkey. Fumadocs' own search is off; the docs search lives in + * components/algolia-search.tsx and handles its own shortcut. + */ +export function AppRootProvider({ children }: { children: ReactNode }) { + return ( + <RootProvider + search={{ enabled: false }} + theme={{ defaultTheme: "dark", hotKey: false }} + > + {children} + </RootProvider> + ); +} diff --git a/app/_components/sample-app-card.tsx b/components/sample-app-card.tsx similarity index 100% rename from app/_components/sample-app-card.tsx rename to components/sample-app-card.tsx diff --git a/app/_components/scope-picker.tsx b/components/scope-picker.tsx similarity index 100% rename from app/_components/scope-picker.tsx rename to components/scope-picker.tsx diff --git a/app/_components/starter-tool-info.tsx b/components/starter-tool-info.tsx similarity index 95% rename from app/_components/starter-tool-info.tsx rename to components/starter-tool-info.tsx index 0af7d29b4..1ccb286ce 100644 --- a/app/_components/starter-tool-info.tsx +++ b/components/starter-tool-info.tsx @@ -1,5 +1,5 @@ -import { Callout } from "nextra/components"; import type React from "react"; +import { Callout } from "@/components/mdx/callout"; type StarterToolInfoProps = { toolkitName: string; diff --git a/components/subpage-list.tsx b/components/subpage-list.tsx new file mode 100644 index 000000000..c43cb260d --- /dev/null +++ b/components/subpage-list.tsx @@ -0,0 +1,124 @@ +import type * as PageTree from "fumadocs-core/page-tree"; +import Link from "next/link"; +import { source } from "@/lib/source"; + +type SubpageListProps = { + /** URL of the parent folder, e.g. `/en/operate/identity` or `/operate/identity`. */ + basePath: string; +}; + +const TRAILING_SLASHES_REGEX = /\/+$/; + +function normalizeUrl(url: string): string { + const withLocale = + url.startsWith("/en/") || url === "/en" ? url : `/en${url}`; + return withLocale.replace(TRAILING_SLASHES_REGEX, ""); +} + +/** + * URLs that identify a folder: its `index` page, or a direct child page at the + * folder path. Fumadocs only sets `folder.index` when `index` is not listed + * explicitly in meta.json; our generated meta files list it, so check both. + */ +function folderUrls(folder: PageTree.Folder): Set<string> { + const urls = new Set<string>(); + if (folder.index?.url) { + urls.add(normalizeUrl(folder.index.url)); + } + for (const child of folder.children) { + if (child.type === "page") { + urls.add(normalizeUrl(child.url)); + } + } + return urls; +} + +function findFolder( + nodes: PageTree.Node[], + target: string +): PageTree.Folder | null { + for (const node of nodes) { + if (node.type !== "folder") { + continue; + } + if (folderUrls(node).has(target)) { + return node; + } + const nested = findFolder(node.children, target); + if (nested) { + return nested; + } + } + return null; +} + +/** Child entries of a folder, in sidebar order: pages and sub-folders (their index page). */ +function childLinks( + folder: PageTree.Folder, + target: string +): { url: string; name: string }[] { + const links: { url: string; name: string }[] = []; + for (const child of folder.children) { + if (child.type === "page") { + if (normalizeUrl(child.url) === target) { + continue; + } + links.push({ name: nodeName(child.name), url: child.url }); + } else if (child.type === "folder") { + const url = child.index?.url ?? firstPageUrl(child); + if (url) { + links.push({ name: nodeName(child.name), url }); + } + } + } + return links; +} + +function firstPageUrl(folder: PageTree.Folder): string | null { + for (const child of folder.children) { + if (child.type === "page") { + return child.url; + } + if (child.type === "folder") { + const url = child.index?.url ?? firstPageUrl(child); + if (url) { + return url; + } + } + } + return null; +} + +function nodeName(name: PageTree.Node["name"]): string { + return typeof name === "string" ? name : String(name ?? ""); +} + +/** + * Lists the child pages of a docs folder as a bullet list. Children are read + * from the Fumadocs page tree, so only `basePath` is needed. + */ +export function SubpageList({ basePath }: SubpageListProps) { + const target = normalizeUrl(basePath); + const tree = source.getPageTree(); + const folder = findFolder(tree.children, target); + const links = folder ? childLinks(folder, target) : []; + + if (links.length === 0) { + return null; + } + + return ( + <ul className="ms-[1.5em] not-first:mt-[1.25em] list-disc [:is(ol,ul)_&]:my-[.75em]"> + {links.map((link) => ( + <li className="my-[.5em]" key={link.url}> + <Link + className="text-(--x-color-primary-600) underline decoration-from-font [text-underline-position:from-font] hover:no-underline focus-visible:nextra-focus" + href={link.url} + > + {link.name} + </Link> + </li> + ))} + </ul> + ); +} diff --git a/app/_components/tabbed-code-block.tsx b/components/tabbed-code-block.tsx similarity index 100% rename from app/_components/tabbed-code-block.tsx rename to components/tabbed-code-block.tsx diff --git a/app/_components/tabbed-code-block/code-skeleton.tsx b/components/tabbed-code-block/code-skeleton.tsx similarity index 100% rename from app/_components/tabbed-code-block/code-skeleton.tsx rename to components/tabbed-code-block/code-skeleton.tsx diff --git a/app/_components/tabbed-code-block/copy-button.tsx b/components/tabbed-code-block/copy-button.tsx similarity index 100% rename from app/_components/tabbed-code-block/copy-button.tsx rename to components/tabbed-code-block/copy-button.tsx diff --git a/components/tabbed-code-block/language-tabs.tsx b/components/tabbed-code-block/language-tabs.tsx new file mode 100644 index 000000000..2f156f6b8 --- /dev/null +++ b/components/tabbed-code-block/language-tabs.tsx @@ -0,0 +1,61 @@ +"use client"; +import { Button } from "@arcadeai/design-system"; +import { cn } from "@arcadeai/design-system/lib/utils"; +import { useCallback } from "react"; + +function LanguageTab({ + lang, + isActive, + onSelect, +}: { + lang: string; + isActive: boolean; + onSelect: (language: string) => void; +}) { + const handleClick = useCallback(() => onSelect(lang), [lang, onSelect]); + return ( + <Button + aria-label={`Switch to ${lang} example`} + aria-selected={isActive} + className={cn( + "h-9 px-4 font-medium text-sm focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background", + isActive + ? "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90 hover:text-primary-foreground active:bg-primary/80 active:text-primary-foreground" + : "text-muted-foreground hover:bg-primary/60 hover:text-foreground active:bg-primary/60 active:text-foreground" + )} + onClick={handleClick} + role="tab" + size="sm" + variant="secondary" + > + {lang} + </Button> + ); +} + +export function LanguageTabs({ + languages, + currentLanguage, + onSelect, +}: { + languages: string[]; + currentLanguage: string; + onSelect: (language: string) => void; +}) { + return ( + <div + aria-label="Programming languages" + className="inline-flex items-center gap-1.5" + role="tablist" + > + {languages.map((lang) => ( + <LanguageTab + isActive={currentLanguage === lang} + key={lang} + lang={lang} + onSelect={onSelect} + /> + ))} + </div> + ); +} diff --git a/app/_components/tabbed-code-block/tab-select.tsx b/components/tabbed-code-block/tab-select.tsx similarity index 100% rename from app/_components/tabbed-code-block/tab-select.tsx rename to components/tabbed-code-block/tab-select.tsx diff --git a/app/_components/tabbed-code-block/use-example-file.ts b/components/tabbed-code-block/use-example-file.ts similarity index 100% rename from app/_components/tabbed-code-block/use-example-file.ts rename to components/tabbed-code-block/use-example-file.ts diff --git a/app/_components/table-of-contents.tsx b/components/table-of-contents.tsx similarity index 100% rename from app/_components/table-of-contents.tsx rename to components/table-of-contents.tsx diff --git a/app/_components/terminal-code-block.tsx b/components/terminal-code-block.tsx similarity index 100% rename from app/_components/terminal-code-block.tsx rename to components/terminal-code-block.tsx diff --git a/app/_components/toggle-content.tsx b/components/toggle-content.tsx similarity index 100% rename from app/_components/toggle-content.tsx rename to components/toggle-content.tsx diff --git a/app/_components/tool-card.tsx b/components/tool-card.tsx similarity index 100% rename from app/_components/tool-card.tsx rename to components/tool-card.tsx diff --git a/app/_components/tool-feedback-survey.tsx b/components/tool-feedback-survey.tsx similarity index 100% rename from app/_components/tool-feedback-survey.tsx rename to components/tool-feedback-survey.tsx diff --git a/app/_components/tool-footer.tsx b/components/tool-footer.tsx similarity index 100% rename from app/_components/tool-footer.tsx rename to components/tool-footer.tsx diff --git a/app/_components/tool-info.tsx b/components/tool-info.tsx similarity index 96% rename from app/_components/tool-info.tsx rename to components/tool-info.tsx index f7c34ca5e..ca08378f2 100644 --- a/app/_components/tool-info.tsx +++ b/components/tool-info.tsx @@ -2,8 +2,8 @@ import { Badge, ByocBadge, ProBadge } from "@arcadeai/design-system"; import { usePathname } from "next/navigation"; import type React from "react"; -import { findToolkitFromPath } from "../en/resources/integrations/components/toolkit-utils"; -import { TYPE_CONFIG } from "../en/resources/integrations/components/type-config"; +import { findToolkitFromPath } from "./pages/integrations/toolkit-utils"; +import { TYPE_CONFIG } from "./pages/integrations/type-config"; type ToolInfoProps = { description: string; diff --git a/app/_components/toolkit-docs/components/available-tools-filter.ts b/components/toolkit-docs/components/available-tools-filter.ts similarity index 100% rename from app/_components/toolkit-docs/components/available-tools-filter.ts rename to components/toolkit-docs/components/available-tools-filter.ts diff --git a/app/_components/toolkit-docs/components/available-tools-table.tsx b/components/toolkit-docs/components/available-tools-table.tsx similarity index 100% rename from app/_components/toolkit-docs/components/available-tools-table.tsx rename to components/toolkit-docs/components/available-tools-table.tsx diff --git a/app/_components/toolkit-docs/components/data-table.tsx b/components/toolkit-docs/components/data-table.tsx similarity index 100% rename from app/_components/toolkit-docs/components/data-table.tsx rename to components/toolkit-docs/components/data-table.tsx diff --git a/app/_components/toolkit-docs/components/documentation-chunk-renderer.tsx b/components/toolkit-docs/components/documentation-chunk-renderer.tsx similarity index 87% rename from app/_components/toolkit-docs/components/documentation-chunk-renderer.tsx rename to components/toolkit-docs/components/documentation-chunk-renderer.tsx index 64e7de092..91b3a847f 100644 --- a/app/_components/toolkit-docs/components/documentation-chunk-renderer.tsx +++ b/components/toolkit-docs/components/documentation-chunk-renderer.tsx @@ -1,30 +1,32 @@ "use client"; import { evaluate } from "@mdx-js/mdx"; -import { Callout, Steps, Tabs } from "nextra/components"; import type React from "react"; import { useEffect, useMemo, useState } from "react"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; import remarkGfm from "remark-gfm"; -import { SignupLink } from "../../analytics"; -import TabbedCodeBlock from "../../tabbed-code-block"; -import TableOfContents from "../../table-of-contents"; -import ToolFooter from "../../tool-footer"; +import { SignupLink } from "@/components/analytics"; +import { Callout } from "@/components/mdx/callout"; +import { Steps } from "@/components/mdx/steps"; +import { Tabs } from "@/components/mdx/tabs"; +import TabbedCodeBlock from "@/components/tabbed-code-block"; +import TableOfContents from "@/components/table-of-contents"; +import ToolFooter from "@/components/tool-footer"; import type { DocumentationChunk, DocumentationChunkLocation, DocumentationChunkPosition, DocumentationChunkRendererProps, } from "../types"; -import DataTable from "./data-table"; +import { DataTable } from "./data-table"; import { createMdxCache } from "./mdx-cache"; // Regex for removing leading ## from headers (defined at top level for performance) const HEADER_PREFIX_REGEX = /^#+\s*/; /** - * Maps chunk types to Nextra Callout types + * Maps chunk types to Callout types */ const CALLOUT_TYPE_MAP: Record< string, @@ -38,7 +40,7 @@ const CALLOUT_TYPE_MAP: Record< }; /** - * Maps chunk variants to Nextra Callout types + * Maps chunk variants to Callout types */ const VARIANT_TYPE_MAP: Record< string, @@ -173,7 +175,7 @@ function MdxContent({ content }: { content: string }) { } return ( - <div className="prose prose-sm dark:prose-invert max-w-none"> + <div className="tk-prose tk-prose-sm dark:tk-prose-invert max-w-none"> <Component components={MDX_COMPONENTS} /> </div> ); @@ -298,6 +300,23 @@ export function sortChunksDeterministically( }); } +/** + * Chunks carry no id, so key them by their identifying content (the same + * fields the deterministic sort uses). Exact duplicates get a running suffix + * so keys stay unique without depending on array position. + */ +function withChunkKeys( + chunks: readonly DocumentationChunk[] +): Array<{ chunk: DocumentationChunk; key: string }> { + const seen = new Map<string, number>(); + return chunks.map((chunk) => { + const base = `${chunk.type}:${chunk.header ?? ""}:${chunk.content}`; + const occurrence = seen.get(base) ?? 0; + seen.set(base, occurrence + 1); + return { chunk, key: occurrence === 0 ? base : `${base}#${occurrence}` }; + }); +} + /** * DocumentationChunkRenderer * @@ -345,11 +364,8 @@ export function DocumentationChunkRenderer({ return ( <div className={className} data-testid="documentation-chunk-renderer"> - {sortedChunks.map((chunk, index) => ( - <ChunkContent - chunk={chunk} - key={`${chunk.location}-${chunk.position}-${index}`} - /> + {withChunkKeys(sortedChunks).map(({ chunk, key }) => ( + <ChunkContent chunk={chunk} key={key} /> ))} </div> ); @@ -368,5 +384,3 @@ export function hasChunksAt( (chunk) => chunk.location === location && chunk.position === position ); } - -export default DocumentationChunkRenderer; diff --git a/app/_components/toolkit-docs/components/dynamic-code-block.tsx b/components/toolkit-docs/components/dynamic-code-block.tsx similarity index 100% rename from app/_components/toolkit-docs/components/dynamic-code-block.tsx rename to components/toolkit-docs/components/dynamic-code-block.tsx diff --git a/app/_components/toolkit-docs/components/index.ts b/components/toolkit-docs/components/index.ts similarity index 100% rename from app/_components/toolkit-docs/components/index.ts rename to components/toolkit-docs/components/index.ts diff --git a/app/_components/toolkit-docs/components/mdx-cache.ts b/components/toolkit-docs/components/mdx-cache.ts similarity index 100% rename from app/_components/toolkit-docs/components/mdx-cache.ts rename to components/toolkit-docs/components/mdx-cache.ts diff --git a/app/_components/toolkit-docs/components/page-actions.tsx b/components/toolkit-docs/components/page-actions.tsx similarity index 100% rename from app/_components/toolkit-docs/components/page-actions.tsx rename to components/toolkit-docs/components/page-actions.tsx diff --git a/app/_components/toolkit-docs/components/parameters-table.tsx b/components/toolkit-docs/components/parameters-table.tsx similarity index 100% rename from app/_components/toolkit-docs/components/parameters-table.tsx rename to components/toolkit-docs/components/parameters-table.tsx diff --git a/app/_components/toolkit-docs/components/scopes-display.tsx b/components/toolkit-docs/components/scopes-display.tsx similarity index 100% rename from app/_components/toolkit-docs/components/scopes-display.tsx rename to components/toolkit-docs/components/scopes-display.tsx diff --git a/app/_components/toolkit-docs/components/tool-metadata-section.tsx b/components/toolkit-docs/components/tool-metadata-section.tsx similarity index 100% rename from app/_components/toolkit-docs/components/tool-metadata-section.tsx rename to components/toolkit-docs/components/tool-metadata-section.tsx diff --git a/app/_components/toolkit-docs/components/tool-section.tsx b/components/toolkit-docs/components/tool-section.tsx similarity index 100% rename from app/_components/toolkit-docs/components/tool-section.tsx rename to components/toolkit-docs/components/tool-section.tsx diff --git a/app/_components/toolkit-docs/components/toolkit-docs-preview.tsx b/components/toolkit-docs/components/toolkit-docs-preview.tsx similarity index 90% rename from app/_components/toolkit-docs/components/toolkit-docs-preview.tsx rename to components/toolkit-docs/components/toolkit-docs-preview.tsx index 41f687d12..bba4d176e 100644 --- a/app/_components/toolkit-docs/components/toolkit-docs-preview.tsx +++ b/components/toolkit-docs/components/toolkit-docs-preview.tsx @@ -1,4 +1,4 @@ -import { readToolkitData } from "@/app/_lib/toolkit-data"; +import { readToolkitData } from "@/lib/toolkit-data"; import type { ToolkitData } from "../types"; import { ToolkitPage } from "./toolkit-page"; diff --git a/app/_components/toolkit-docs/components/toolkit-header.tsx b/components/toolkit-docs/components/toolkit-header.tsx similarity index 98% rename from app/_components/toolkit-docs/components/toolkit-header.tsx rename to components/toolkit-docs/components/toolkit-header.tsx index 574bb0d31..0ad29285c 100644 --- a/app/_components/toolkit-docs/components/toolkit-header.tsx +++ b/components/toolkit-docs/components/toolkit-header.tsx @@ -4,7 +4,7 @@ import { Badge, ByocBadge, ProBadge } from "@arcadeai/design-system"; import { KeyRound, Wrench } from "lucide-react"; import type React from "react"; -import { TYPE_CONFIG } from "../../../en/resources/integrations/components/type-config"; +import { TYPE_CONFIG } from "@/components/pages/integrations/type-config"; import { AUTH_TYPE_LABELS, DEFAULT_AUTHOR, diff --git a/app/_components/toolkit-docs/components/toolkit-page-utils.ts b/components/toolkit-docs/components/toolkit-page-utils.ts similarity index 100% rename from app/_components/toolkit-docs/components/toolkit-page-utils.ts rename to components/toolkit-docs/components/toolkit-page-utils.ts diff --git a/app/_components/toolkit-docs/components/toolkit-page.tsx b/components/toolkit-docs/components/toolkit-page.tsx similarity index 90% rename from app/_components/toolkit-docs/components/toolkit-page.tsx rename to components/toolkit-docs/components/toolkit-page.tsx index f599ddbca..4520d772b 100644 --- a/app/_components/toolkit-docs/components/toolkit-page.tsx +++ b/components/toolkit-docs/components/toolkit-page.tsx @@ -6,7 +6,7 @@ import dynamic from "next/dynamic"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import ReactMarkdown from "react-markdown"; -import ToolFooter from "../../tool-footer"; +import ToolFooter from "@/components/tool-footer"; import { getPackageName, TOOL_METADATA_FALLBACK_STYLE, @@ -53,6 +53,17 @@ const ToolkitToolDetail = dynamic( { ssr: false } ); +const scrollToTop = () => { + window.scrollTo({ behavior: "smooth", top: 0 }); +}; + +const scrollToBottom = () => { + window.scrollTo({ + behavior: "smooth", + top: document.documentElement.scrollHeight, + }); +}; + /** * Floating buttons to scroll to top/bottom of the page. * Only shows when user has scrolled past a threshold. @@ -81,17 +92,6 @@ function ScrollToButtons() { return () => window.removeEventListener("scroll", handleScroll); }, []); - const scrollToTop = () => { - window.scrollTo({ top: 0, behavior: "smooth" }); - }; - - const scrollToBottom = () => { - window.scrollTo({ - top: document.documentElement.scrollHeight, - behavior: "smooth", - }); - }; - if (!showButtons) { return null; } @@ -108,7 +108,7 @@ function ScrollToButtons() { > <ArrowUp className="h-5 w-5" /> </Button> - {!atBottom && ( + {atBottom ? null : ( <Button aria-label="Scroll to bottom" className="h-10 w-10 rounded-full shadow-lg backdrop-blur-sm" @@ -260,6 +260,38 @@ function BreadcrumbBar({ ); } +type SetItemRef = (id: string, el: HTMLAnchorElement | null) => void; + +/** + * Sidebar anchor that registers itself with the parent's ref map through a + * per-id stable callback ref. + */ +function TocLink({ + id, + href, + className, + title, + setItemRef, + children, +}: { + id: string; + href: string; + className: string; + title?: string; + setItemRef: SetItemRef; + children: React.ReactNode; +}) { + const ref = useCallback( + (el: HTMLAnchorElement | null) => setItemRef(id, el), + [id, setItemRef] + ); + return ( + <a className={className} href={href} ref={ref} title={title}> + {children} + </a> + ); +} + /** * Right sidebar that lists the tools on the page. * Structure: Fixed header/footer with scrollable middle tool list. @@ -402,7 +434,7 @@ function ToolsOnThisPage({ } }, [activeId]); - const setItemRef = useCallback((id: string, el: HTMLAnchorElement | null) => { + const setItemRef = useCallback<SetItemRef>((id, el) => { if (el) { itemRefs.current.set(id, el); } else { @@ -410,6 +442,12 @@ function ToolsOnThisPage({ } }, []); + const handleQueryChange = useCallback( + (event: React.ChangeEvent<HTMLInputElement>) => + setQuery(event.target.value), + [] + ); + const getLinkClasses = (id: string) => { const isActive = activeId === id; return isActive @@ -426,7 +464,7 @@ function ToolsOnThisPage({ <input aria-label="Search tools on this page" className="w-full rounded-lg border border-muted/60 bg-background px-3 py-2 text-sm transition-colors placeholder:text-muted-foreground/70 focus:border-brand-accent focus:outline-none dark:border-neutral-dark-high dark:bg-neutral-dark/60" - onChange={(event) => setQuery(event.target.value)} + onChange={handleQueryChange} placeholder="Search tools..." type="search" value={query} @@ -437,39 +475,43 @@ function ToolsOnThisPage({ </div> {/* Navigation links */} <div className="mt-4 space-y-2"> - <a + <TocLink className={`block text-sm transition-colors ${getLinkClasses(TOOLKIT_PAGE_OVERVIEW_LINK.id)}`} href={TOOLKIT_PAGE_OVERVIEW_LINK.href} - ref={(el) => setItemRef(TOOLKIT_PAGE_OVERVIEW_LINK.id, el)} + id={TOOLKIT_PAGE_OVERVIEW_LINK.id} + setItemRef={setItemRef} > {TOOLKIT_PAGE_OVERVIEW_LINK.label} - </a> + </TocLink> {/* Custom documentation sections */} {customSections.map((section) => ( - <a + <TocLink className={`block pl-3 text-sm transition-colors ${getLinkClasses(section.id)}`} href={section.href} + id={section.id} key={section.id} - ref={(el) => setItemRef(section.id, el)} + setItemRef={setItemRef} > {section.label} - </a> + </TocLink> ))} - <a + <TocLink className={`block text-sm transition-colors ${getLinkClasses(TOOLKIT_PAGE_AVAILABLE_TOOLS_LINK.id)}`} href={TOOLKIT_PAGE_AVAILABLE_TOOLS_LINK.href} - ref={(el) => setItemRef(TOOLKIT_PAGE_AVAILABLE_TOOLS_LINK.id, el)} + id={TOOLKIT_PAGE_AVAILABLE_TOOLS_LINK.id} + setItemRef={setItemRef} > {TOOLKIT_PAGE_AVAILABLE_TOOLS_LINK.label} - </a> - <a + </TocLink> + <TocLink className={`block text-sm transition-colors ${getLinkClasses(TOOLKIT_PAGE_SELECTED_TOOLS_LINK.id)}`} href={TOOLKIT_PAGE_SELECTED_TOOLS_LINK.href} - ref={(el) => setItemRef(TOOLKIT_PAGE_SELECTED_TOOLS_LINK.id, el)} + id={TOOLKIT_PAGE_SELECTED_TOOLS_LINK.id} + setItemRef={setItemRef} > {TOOLKIT_PAGE_SELECTED_TOOLS_LINK.label} ({selectedToolsList.length} ) - </a> + </TocLink> </div> </div> @@ -483,15 +525,16 @@ function ToolsOnThisPage({ {filteredTools.map((tool) => { const toolId = toToolAnchorId(tool.qualifiedName); return ( - <a + <TocLink className={`block truncate py-1 pl-3 text-sm transition-colors ${getLinkClasses(toolId)}`} href={`#${toolId}`} + id={toolId} key={tool.qualifiedName} - ref={(el) => setItemRef(toolId, el)} + setItemRef={setItemRef} title={tool.qualifiedName} > {tool.qualifiedName} - </a> + </TocLink> ); })} </div> @@ -500,13 +543,14 @@ function ToolsOnThisPage({ {/* Footer section */} <div className="border-muted/60 border-t px-6 py-4 dark:border-neutral-dark-high/20"> - <a + <TocLink className={`block text-sm transition-colors ${getLinkClasses(TOOLKIT_PAGE_GET_BUILDING_LINK.id)}`} href={TOOLKIT_PAGE_GET_BUILDING_LINK.href} - ref={(el) => setItemRef(TOOLKIT_PAGE_GET_BUILDING_LINK.id, el)} + id={TOOLKIT_PAGE_GET_BUILDING_LINK.id} + setItemRef={setItemRef} > {TOOLKIT_PAGE_GET_BUILDING_LINK.label} - </a> + </TocLink> </div> </aside> ); @@ -535,7 +579,7 @@ export function ToolkitPage({ data }: ToolkitPageProps) { return () => window.removeEventListener("hashchange", update); }, []); - const tools = data.tools ?? []; + const { tools } = data; const documentationChunks = data.documentationChunks ?? []; const [selectedTools, setSelectedTools] = useState<Set<string>>(new Set()); const shouldShowSelection = tools.length > 0; @@ -564,11 +608,11 @@ export function ToolkitPage({ data }: ToolkitPageProps) { [tools] ); - const handleScopeSelectionChange = (toolNames: string[]) => { + const handleScopeSelectionChange = useCallback((toolNames: string[]) => { setSelectedTools(new Set(toolNames)); - }; + }, []); - const toggleToolSelection = (toolName: string) => { + const toggleToolSelection = useCallback((toolName: string) => { setSelectedTools((prevSelected) => { const nextSelected = new Set(prevSelected); if (nextSelected.has(toolName)) { @@ -578,10 +622,10 @@ export function ToolkitPage({ data }: ToolkitPageProps) { } return nextSelected; }); - }; + }, []); return ( - <div className="w-full"> + <div className="preflight-borders w-full"> {/* Overview section */} <section id={TOOLKIT_PAGE_OVERVIEW_LINK.id}> <BreadcrumbBar category={data.metadata?.category} label={data.label} /> @@ -639,8 +683,8 @@ export function ToolkitPage({ data }: ToolkitPageProps) { position="after" /> - {data.summary && ( - <div className="prose prose-sm dark:prose-invert mt-6 max-w-none text-foreground"> + {data.summary ? ( + <div className="tk-prose tk-prose-sm dark:tk-prose-invert mt-6 max-w-none text-foreground"> <ReactMarkdown components={{ h1: "h2" }} rehypePlugins={[rehypeNeutralizeEmails]} @@ -648,7 +692,7 @@ export function ToolkitPage({ data }: ToolkitPageProps) { {data.summary} </ReactMarkdown> </div> - )} + ) : null} <DocumentationChunkRenderer chunks={documentationChunks} @@ -773,5 +817,3 @@ export function ToolkitPage({ data }: ToolkitPageProps) { </div> ); } - -export default ToolkitPage; diff --git a/app/_components/toolkit-docs/components/toolkit-tool-detail.tsx b/components/toolkit-docs/components/toolkit-tool-detail.tsx similarity index 100% rename from app/_components/toolkit-docs/components/toolkit-tool-detail.tsx rename to components/toolkit-docs/components/toolkit-tool-detail.tsx diff --git a/app/_components/toolkit-docs/components/use-toolkit-detail.ts b/components/toolkit-docs/components/use-toolkit-detail.ts similarity index 100% rename from app/_components/toolkit-docs/components/use-toolkit-detail.ts rename to components/toolkit-docs/components/use-toolkit-detail.ts diff --git a/app/_components/toolkit-docs/constants.ts b/components/toolkit-docs/constants.ts similarity index 100% rename from app/_components/toolkit-docs/constants.ts rename to components/toolkit-docs/constants.ts diff --git a/app/_components/toolkit-docs/index.ts b/components/toolkit-docs/index.ts similarity index 98% rename from app/_components/toolkit-docs/index.ts rename to components/toolkit-docs/index.ts index 415e442c4..9e4b2e6d2 100644 --- a/app/_components/toolkit-docs/index.ts +++ b/components/toolkit-docs/index.ts @@ -7,7 +7,7 @@ * * @example * ```tsx - * import { ToolkitHeader, DocumentationChunkRenderer } from '@/app/_components/toolkit-docs'; + * import { ToolkitHeader, DocumentationChunkRenderer } from '@/components/toolkit-docs'; * import toolkitData from '@/toolkit-docs-generator/data/toolkits/github.json'; * * export default function GitHubPage() { diff --git a/app/_components/toolkit-docs/lib/neutralize-emails.tsx b/components/toolkit-docs/lib/neutralize-emails.tsx similarity index 100% rename from app/_components/toolkit-docs/lib/neutralize-emails.tsx rename to components/toolkit-docs/lib/neutralize-emails.tsx diff --git a/app/_components/toolkit-docs/types/index.ts b/components/toolkit-docs/types/index.ts similarity index 100% rename from app/_components/toolkit-docs/types/index.ts rename to components/toolkit-docs/types/index.ts diff --git a/app/en/build/arcade-deploy/page.mdx b/content/docs/build/arcade-deploy.mdx similarity index 85% rename from app/en/build/arcade-deploy/page.mdx rename to content/docs/build/arcade-deploy.mdx index 330edd6bd..f8546e42b 100644 --- a/app/en/build/arcade-deploy/page.mdx +++ b/content/docs/build/arcade-deploy.mdx @@ -1,17 +1,16 @@ --- -title: "Cloud deployments with Arcade Deploy" -description: "Learn how to deploy a worker with Arcade Deploy" +title: Deploying to the cloud with Arcade Deploy +metaTitle: Cloud deployments with Arcade Deploy +sidebarTitle: Arcade Deploy +description: Learn how to deploy a worker with Arcade Deploy --- -import { Steps, Tabs, Callout } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; - -# Deploying to the cloud with Arcade Deploy +import { SignupLink } from "@/components/analytics"; Running your MCP servers locally is very convenient during development and testing. Once your MCP server is mature, however, you may want to access it from any MCP client, or to facilitate multi-user support. Doing all that from your computer comes with the complexity of running and maintaining a server, handling auth and high availability for all your users and all the integrations you want to support. Arcade Deploy takes care of all that for you. Your MCP server will be registered to Arcade, adding all the tools you created to the larger tool catalog. From there, you can create MCP Gateways to pick and choose which tools you want to use in your MCP clients, which can be from any connected MCP server. <Callout type="info"> -Arcade Deploy hosts *your* MCP server, either on Arcade Cloud or in a self-hosted Arcade installation on your own Kubernetes cluster. It's a feature for serving tools, not a way to deploy the Arcade platform. For a full platform deployment, see the [marketplace guides](/operate/deploy) or [self-host with Helm](/operate/deploy/helm). To turn on Arcade Deploy in a self-hosted installation, see [Arcade Deploy on your own cluster](/operate/deploy/helm-arcade-deploy). +Arcade Deploy hosts *your* MCP server, either on Arcade Cloud or in a self-hosted Arcade installation on your own Kubernetes cluster. It's a feature for serving tools, not a way to deploy the Arcade platform. For a full platform deployment, see the [marketplace guides](/en/operate/deploy) or [self-host with Helm](/en/operate/deploy/helm). To turn on Arcade Deploy in a self-hosted installation, see [Arcade Deploy on your own cluster](/en/operate/deploy/helm-arcade-deploy). </Callout> <GuideOverview> @@ -25,7 +24,7 @@ This guide shows you how to deploy your MCP Server with Arcade Deploy. - <DashboardLink path="register" /> - [uv package manager](https://docs.astral.sh/uv/getting-started/installation/) -- [Create an MCP Server](/build/create-tools/tool-basics/build-mcp-server) +- [Create an MCP Server](/en/build/create-tools/tool-basics/build-mcp-server) </GuideOverview.Prerequisites> @@ -64,12 +63,12 @@ This guide shows you how to deploy your MCP Server with Arcade Deploy. ## Create an MCP server using Arcade MCP -If you have not created an MCP server yet, then follow the steps outlined in [this guide](/build/create-tools/tool-basics/build-mcp-server) before deploying. +If you have not created an MCP server yet, then follow the steps outlined in [this guide](/en/build/create-tools/tool-basics/build-mcp-server) before deploying. ## Deploy your MCP Server <Callout type="info"> -Deploying to a self-hosted Arcade installation? Sign in to it first with `arcade login --url <your-arcade-url>`. `arcade deploy` targets the installation you're signed in to. Your platform operator turns deployments on as described in [Arcade Deploy on your own cluster](/operate/deploy/helm-arcade-deploy). +Deploying to a self-hosted Arcade installation? Sign in to it first with `arcade login --url <your-arcade-url>`. `arcade deploy` targets the installation you're signed in to. Your platform operator turns deployments on as described in [Arcade Deploy on your own cluster](/en/operate/deploy/helm-arcade-deploy). </Callout> If you run `arcade deploy` before signing in, it exits with an error that names the `arcade login` command to run first, and uploads nothing. A deploy with `ARCADE_URL` and `ARCADE_API_KEY` set, for example in CI, doesn't need a sign-in. @@ -158,17 +157,17 @@ Navigate to the [Servers](https://api.arcade.dev/dashboard/servers) page in your - Test and execute all the tools - Manage users connected to the Auth providers - Manage the secrets for the server -- Create [MCP Gateways](/operate/governance/mcp-gateways) +- Create [MCP Gateways](/en/operate/governance/mcp-gateways) ## Create an MCP Gateway to call the tools in your MCP Server -Once the MCP server is deployed to Arcade, all the tools in the server will be available in the [tool catalog](https://api.arcade.dev/dashboard/tools) page in your Arcade dashboard. To call the tools from an MCP client, you first need to [create an MCP Gateway](/operate/governance/mcp-gateways) to pick and choose which tools you want to use in your MCP clients. +Once the MCP server is deployed to Arcade, all the tools in the server will be available in the [tool catalog](https://api.arcade.dev/dashboard/tools) page in your Arcade dashboard. To call the tools from an MCP client, you first need to [create an MCP Gateway](/en/operate/governance/mcp-gateways) to pick and choose which tools you want to use in your MCP clients. When creating an MCP gateway, you can select the tools you want to include in the Gateway from any MCP Servers available to the project, including the one you just deployed. ## Use Arcade clients to call the tools in your MCP Server -You can use any of the available [Arcade clients](/references) to call the tools in your MCP Server. When using the clients, you are not required to create an MCP Gateway, as the client will handle the connection to all tools in your Arcade project directly. +You can use any of the available [Arcade clients](/en/references) to call the tools in your MCP Server. When using the clients, you are not required to create an MCP Gateway, as the client will handle the connection to all tools in your Arcade project directly. </Steps> diff --git a/app/en/build/create-tools/error-handling/page.mdx b/content/docs/build/create-tools/error-handling/index.mdx similarity index 58% rename from app/en/build/create-tools/error-handling/page.mdx rename to content/docs/build/create-tools/error-handling/index.mdx index 542b723e6..35f8df360 100644 --- a/app/en/build/create-tools/error-handling/page.mdx +++ b/content/docs/build/create-tools/error-handling/index.mdx @@ -1,15 +1,13 @@ --- -title: "Handle Errors" -description: "Learn how to implement robust error handling in your tools for better user experience" +title: Handle Errors +sidebarTitle: Overview +description: Learn how to implement robust error handling in your tools for better user experience --- -import { SubpageList } from '../../../../_components/subpage-list'; -import meta from './_meta'; - -# Handle Errors +import { SubpageList } from '@/components/subpage-list'; Learn how to implement robust error handling that improves user experience and enables smart retry behavior. Apply these patterns as you develop tools to make them more resilient and user-friendly in production. Robust error handling is crucial for building reliable tools that provide great user experiences. Arcade provides a comprehensive error handling system that helps you manage different types of errors gracefully. -<SubpageList basePath="/build/create-tools/error-handling" meta={meta} /> \ No newline at end of file +<SubpageList basePath="/build/create-tools/error-handling" /> \ No newline at end of file diff --git a/content/docs/build/create-tools/error-handling/meta.json b/content/docs/build/create-tools/error-handling/meta.json new file mode 100644 index 000000000..fd24e393a --- /dev/null +++ b/content/docs/build/create-tools/error-handling/meta.json @@ -0,0 +1,8 @@ +{ + "title": "Handle errors", + "pages": [ + "index", + "retry-tools", + "useful-tool-errors" + ] +} diff --git a/app/en/build/create-tools/error-handling/retry-tools/page.mdx b/content/docs/build/create-tools/error-handling/retry-tools.mdx similarity index 93% rename from app/en/build/create-tools/error-handling/retry-tools/page.mdx rename to content/docs/build/create-tools/error-handling/retry-tools.mdx index e6f0623ec..760d253ca 100644 --- a/app/en/build/create-tools/error-handling/retry-tools/page.mdx +++ b/content/docs/build/create-tools/error-handling/retry-tools.mdx @@ -1,10 +1,10 @@ --- -title: "Retry Tools with Improved Prompt" -description: "Documentation for retrying tools with improved prompts in the Arcade Tool SDK" +title: RetryableToolError in Arcade +metaTitle: Retry Tools with Improved Prompt +sidebarTitle: Retry Tools with Improved Prompt +description: Documentation for retrying tools with improved prompts in the Arcade Tool SDK --- -# RetryableToolError in Arcade - Sometimes you may want to retry a tool call with additional context to improve the tool call's input parameters predicted by the model and try again. You can do this by raising a `RetryableToolError` within the tool. ### Understanding RetryableToolError diff --git a/app/en/build/create-tools/error-handling/useful-tool-errors/page.mdx b/content/docs/build/create-tools/error-handling/useful-tool-errors.mdx similarity index 95% rename from app/en/build/create-tools/error-handling/useful-tool-errors/page.mdx rename to content/docs/build/create-tools/error-handling/useful-tool-errors.mdx index 4a27dd2fb..9c39ed24d 100644 --- a/app/en/build/create-tools/error-handling/useful-tool-errors/page.mdx +++ b/content/docs/build/create-tools/error-handling/useful-tool-errors.mdx @@ -1,12 +1,11 @@ --- -title: "Provide Useful Tool Errors" -description: "Learn how to provide good errors when building tools with Arcade MCP" +title: Providing useful tool errors +metaTitle: Provide Useful Tool Errors +sidebarTitle: Provide Useful Tool Errors +description: Learn how to provide good errors when building tools with Arcade MCP --- -import { Tabs } from "nextra/components"; -import { ErrorHierarchy } from "@/app/_components/error-hierarchy"; - -# Providing useful tool errors +import { ErrorHierarchy } from "@/components/error-hierarchy"; When building tools with Arcade MCP, understanding error handling is crucial for creating robust and reliable tools. This guide covers everything you need to know about handling errors from a tool developer's perspective. @@ -102,7 +101,7 @@ def search_posts( # ... rest of implementation ``` -Learn more about [RetryableToolError](/build/create-tools/error-handling/retry-tools). +Learn more about [RetryableToolError](/en/build/create-tools/error-handling/retry-tools). ### ContextRequiredToolError @@ -234,4 +233,4 @@ This will raise a `ToolOutputError` because the return type doesn't match the an ## Handling tool errors in agents -To learn more about how to handle tool errors in your Agent, see the [Use Tools](/build/tool-calling/error-handling) section. +To learn more about how to handle tool errors in your Agent, see the [Use Tools](/en/build/tool-calling/error-handling) section. diff --git a/app/en/build/create-tools/evaluate-tools/capture-mode/page.mdx b/content/docs/build/create-tools/evaluate-tools/capture-mode.mdx similarity index 94% rename from app/en/build/create-tools/evaluate-tools/capture-mode/page.mdx rename to content/docs/build/create-tools/evaluate-tools/capture-mode.mdx index d74724e84..95debcf21 100644 --- a/app/en/build/create-tools/evaluate-tools/capture-mode/page.mdx +++ b/content/docs/build/create-tools/evaluate-tools/capture-mode.mdx @@ -1,12 +1,8 @@ --- -title: "Capture mode" -description: "Record tool calls without scoring to bootstrap test expectations" +title: Capture mode +description: Record tool calls without scoring to bootstrap test expectations --- -import { Callout, Steps } from "nextra/components"; - -# Capture mode - Capture mode records tool calls without evaluating them. Use it to bootstrap test expectations or debug model behavior. <Callout type="info"> @@ -383,7 +379,7 @@ ExpectedMCPToolCall("GetWeather", {"location": "Tokyo", "units": "celsius"}) ### Step 3: Add critics -Add critics to validate parameters. See [Critics](/build/create-tools/evaluate-tools/create-evaluation-suite#critics) for options. +Add critics to validate parameters. See [Critics](/en/build/create-tools/evaluate-tools/create-evaluation-suite#critics) for options. ### Step 4: Run evaluations @@ -441,5 +437,5 @@ suite = EvalSuite( ## Next steps -- Learn about [comparative evaluations](/build/create-tools/evaluate-tools/comparative-evaluations) to compare tool sources -- [Create evaluation suites](/build/create-tools/evaluate-tools/create-evaluation-suite) with expectations +- Learn about [comparative evaluations](/en/build/create-tools/evaluate-tools/comparative-evaluations) to compare tool sources +- [Create evaluation suites](/en/build/create-tools/evaluate-tools/create-evaluation-suite) with expectations diff --git a/app/en/build/create-tools/evaluate-tools/comparative-evaluations/page.mdx b/content/docs/build/create-tools/evaluate-tools/comparative-evaluations.mdx similarity index 96% rename from app/en/build/create-tools/evaluate-tools/comparative-evaluations/page.mdx rename to content/docs/build/create-tools/evaluate-tools/comparative-evaluations.mdx index 0575de1ba..a253a5a66 100644 --- a/app/en/build/create-tools/evaluate-tools/comparative-evaluations/page.mdx +++ b/content/docs/build/create-tools/evaluate-tools/comparative-evaluations.mdx @@ -1,14 +1,10 @@ --- -title: "Comparative evaluations" -description: "Compare different tool implementations with the same test cases" +title: Comparative evaluations +description: Compare different tool implementations with the same test cases --- -# Comparative evaluations - Comparative evaluations let you test how well AI models select and use tools from different, isolated tool sources. Each "track" represents a separate tool registry, allowing you to compare implementations side-by-side. -import { Callout, Steps } from "nextra/components"; - ## What are tracks? **Tracks are isolated tool registries** within a single evaluation suite. Each track has its own set of tools that are **not shared** with other tracks. This isolation lets you test how models perform when given different tool options for the same task. @@ -522,7 +518,7 @@ Combine comparative tracks with multiple models: arcade evals . -p openai:gpt-4o,gpt-4o-mini -p anthropic:claude-sonnet-4-5-20250929 ``` -Multi-run flags (`--num-runs`, `--seed`, `--multi-run-pass-rule`) also work with comparative tracks. See [Run evaluations](/build/create-tools/evaluate-tools/run-evaluations). +Multi-run flags (`--num-runs`, `--seed`, `--multi-run-pass-rule`) also work with comparative tracks. See [Run evaluations](/en/build/create-tools/evaluate-tools/run-evaluations). Results show: @@ -752,6 +748,6 @@ suite.add_tool_catalog(catalog_v2, track="Python v2") ## Next steps -- [Create an evaluation suite](/build/create-tools/evaluate-tools/create-evaluation-suite) with tracks -- Use [capture mode](/build/create-tools/evaluate-tools/capture-mode) to discover track-specific tool calls -- [Run evaluations](/build/create-tools/evaluate-tools/run-evaluations) with multiple models and tracks +- [Create an evaluation suite](/en/build/create-tools/evaluate-tools/create-evaluation-suite) with tracks +- Use [capture mode](/en/build/create-tools/evaluate-tools/capture-mode) to discover track-specific tool calls +- [Run evaluations](/en/build/create-tools/evaluate-tools/run-evaluations) with multiple models and tracks diff --git a/app/en/build/create-tools/evaluate-tools/create-evaluation-suite/page.mdx b/content/docs/build/create-tools/evaluate-tools/create-evaluation-suite.mdx similarity index 93% rename from app/en/build/create-tools/evaluate-tools/create-evaluation-suite/page.mdx rename to content/docs/build/create-tools/evaluate-tools/create-evaluation-suite.mdx index c92a7a5bb..bb50fe952 100644 --- a/app/en/build/create-tools/evaluate-tools/create-evaluation-suite/page.mdx +++ b/content/docs/build/create-tools/evaluate-tools/create-evaluation-suite.mdx @@ -1,19 +1,15 @@ --- -title: "Create an evaluation suite" -description: "Learn how to evaluate your tools using Arcade" +title: Create an evaluation suite +description: Learn how to evaluate your tools using Arcade --- -# Create an evaluation suite - Evaluation suites help you test whether AI models use your tools correctly. This guide shows you how to create test cases that measure tool selection and parameter accuracy. -import { Steps, Tabs, Callout } from "nextra/components"; - <Steps> ### Prerequisites -- [Create an MCP Server](/build/create-tools/tool-basics/build-mcp-server) +- [Create an MCP Server](/en/build/create-tools/tool-basics/build-mcp-server) - Install the evaluation dependencies: <Tabs items={["uv", "pip"]} storageKey="packageManager"> @@ -164,7 +160,7 @@ arcade evals . --num-runs 3 --seed random --multi-run-pass-rule majority </Tabs.Tab> </Tabs> -See [Run evaluations](/build/create-tools/evaluate-tools/run-evaluations) for all available options. +See [Run evaluations](/en/build/create-tools/evaluate-tools/run-evaluations) for all available options. ### Understand the results @@ -380,6 +376,6 @@ If you want stricter suites, increase thresholds (for example `fail_threshold=0. ## Next steps -- Learn how to [run evaluations with different providers](/build/create-tools/evaluate-tools/run-evaluations) -- Explore [capture mode](/build/create-tools/evaluate-tools/capture-mode) to record tool calls -- Compare tool sources with [comparative evaluations](/build/create-tools/evaluate-tools/comparative-evaluations) +- Learn how to [run evaluations with different providers](/en/build/create-tools/evaluate-tools/run-evaluations) +- Explore [capture mode](/en/build/create-tools/evaluate-tools/capture-mode) to record tool calls +- Compare tool sources with [comparative evaluations](/en/build/create-tools/evaluate-tools/comparative-evaluations) diff --git a/content/docs/build/create-tools/evaluate-tools/index.mdx b/content/docs/build/create-tools/evaluate-tools/index.mdx new file mode 100644 index 000000000..ddbf95b5e --- /dev/null +++ b/content/docs/build/create-tools/evaluate-tools/index.mdx @@ -0,0 +1,11 @@ +--- +title: Evaluate Tools +sidebarTitle: Overview +description: Learn how to systematically test and improve your tools with Arcade's evaluation framework +--- + +import { SubpageList } from '@/components/subpage-list'; + +Learn how to systematically test and improve your tools to ensure they work reliably in production. Use these techniques after you've built your initial tools to validate their performance and guide iterative improvements. + +<SubpageList basePath="/build/create-tools/evaluate-tools" /> diff --git a/content/docs/build/create-tools/evaluate-tools/meta.json b/content/docs/build/create-tools/evaluate-tools/meta.json new file mode 100644 index 000000000..250212610 --- /dev/null +++ b/content/docs/build/create-tools/evaluate-tools/meta.json @@ -0,0 +1,11 @@ +{ + "title": "Evaluate tools", + "pages": [ + "index", + "why-evaluate", + "create-evaluation-suite", + "run-evaluations", + "capture-mode", + "comparative-evaluations" + ] +} diff --git a/app/en/build/create-tools/evaluate-tools/run-evaluations/page.mdx b/content/docs/build/create-tools/evaluate-tools/run-evaluations.mdx similarity index 95% rename from app/en/build/create-tools/evaluate-tools/run-evaluations/page.mdx rename to content/docs/build/create-tools/evaluate-tools/run-evaluations.mdx index af3215578..d97512830 100644 --- a/app/en/build/create-tools/evaluate-tools/run-evaluations/page.mdx +++ b/content/docs/build/create-tools/evaluate-tools/run-evaluations.mdx @@ -1,14 +1,10 @@ --- -title: "Run evaluations" -description: "Learn how to run evaluations using Arcade" +title: Run evaluations +description: Learn how to run evaluations using Arcade --- -# Run evaluations - The `arcade evals` command discovers and executes evaluation suites with support for multiple providers, models, and output formats. -import { Callout, Tabs } from "nextra/components"; - <Callout type="info"> **Backward compatibility**: All new features (multi-provider support, multi-run evaluation, capture mode, output formats) work with existing evaluation suites. @@ -151,7 +147,7 @@ Capture mode is useful for: - Debugging model behavior - Understanding tool call patterns -See [Capture mode](/build/create-tools/evaluate-tools/capture-mode) for details. +See [Capture mode](/en/build/create-tools/evaluate-tools/capture-mode) for details. ## Output formats @@ -497,5 +493,5 @@ Ensure your evaluation files: ## Next steps -- Explore [capture mode](/build/create-tools/evaluate-tools/capture-mode) for recording tool calls -- Learn about [comparative evaluations](/build/create-tools/evaluate-tools/comparative-evaluations) for comparing tool sources \ No newline at end of file +- Explore [capture mode](/en/build/create-tools/evaluate-tools/capture-mode) for recording tool calls +- Learn about [comparative evaluations](/en/build/create-tools/evaluate-tools/comparative-evaluations) for comparing tool sources \ No newline at end of file diff --git a/app/en/build/create-tools/evaluate-tools/why-evaluate/page.mdx b/content/docs/build/create-tools/evaluate-tools/why-evaluate.mdx similarity index 76% rename from app/en/build/create-tools/evaluate-tools/why-evaluate/page.mdx rename to content/docs/build/create-tools/evaluate-tools/why-evaluate.mdx index f07f96361..62cdb61cd 100644 --- a/app/en/build/create-tools/evaluate-tools/why-evaluate/page.mdx +++ b/content/docs/build/create-tools/evaluate-tools/why-evaluate.mdx @@ -1,12 +1,8 @@ --- -title: "Why evaluate tools?" -description: "Learn why evaluating your tools is important" +title: Why evaluate tools? +description: Learn why evaluating your tools is important --- -import { Callout } from "nextra/components"; - -# Why evaluate tools? - <div className="grid grid-cols-1 md:grid-cols-[4fr_3fr] gap-8"> <div> Tool evaluations ensure AI models use your tools correctly in production. Unlike traditional testing, evaluations measure two key aspects: @@ -65,10 +61,10 @@ FAILED Wrong tool selected -- Score: 50.00% ## Next steps -- [Create an evaluation suite](/build/create-tools/evaluate-tools/create-evaluation-suite) to start testing your tools -- [Run evaluations](/build/create-tools/evaluate-tools/run-evaluations) with multiple providers -- Explore [capture mode](/build/create-tools/evaluate-tools/capture-mode) to bootstrap test expectations -- Compare tool sources with [comparative evaluations](/build/create-tools/evaluate-tools/comparative-evaluations) +- [Create an evaluation suite](/en/build/create-tools/evaluate-tools/create-evaluation-suite) to start testing your tools +- [Run evaluations](/en/build/create-tools/evaluate-tools/run-evaluations) with multiple providers +- Explore [capture mode](/en/build/create-tools/evaluate-tools/capture-mode) to bootstrap test expectations +- Compare tool sources with [comparative evaluations](/en/build/create-tools/evaluate-tools/comparative-evaluations) ## Advanced features @@ -76,12 +72,12 @@ Once you're comfortable with basic evaluations, explore these advanced capabilit ### Capture mode -Record tool calls without scoring to discover what models actually call. Useful for bootstrapping test expectations and debugging. [Learn more →](/build/create-tools/evaluate-tools/capture-mode) +Record tool calls without scoring to discover what models actually call. Useful for bootstrapping test expectations and debugging. [Learn more →](/en/build/create-tools/evaluate-tools/capture-mode) ### Comparative evaluations -Test the same cases against different tool sources (tracks) with isolated registries. Compare how models perform with different tool implementations. [Learn more →](/build/create-tools/evaluate-tools/comparative-evaluations) +Test the same cases against different tool sources (tracks) with isolated registries. Compare how models perform with different tool implementations. [Learn more →](/en/build/create-tools/evaluate-tools/comparative-evaluations) ### Output formats -Save results in multiple formats (txt, md, html, json) for reporting and analysis. Specify output files with extensions or use no extension for all formats. [Learn more →](/build/create-tools/evaluate-tools/run-evaluations#output-formats) +Save results in multiple formats (txt, md, html, json) for reporting and analysis. Specify output files with extensions or use no extension for all formats. [Learn more →](/en/build/create-tools/evaluate-tools/run-evaluations#output-formats) diff --git a/content/docs/build/create-tools/improve/meta.json b/content/docs/build/create-tools/improve/meta.json new file mode 100644 index 000000000..77cf6d7a9 --- /dev/null +++ b/content/docs/build/create-tools/improve/meta.json @@ -0,0 +1,3 @@ +{ + "title": "Improve an existing toolkit" +} diff --git a/app/en/build/create-tools/improve/types-of-tools/page.mdx b/content/docs/build/create-tools/improve/types-of-tools.mdx similarity index 90% rename from app/en/build/create-tools/improve/types-of-tools/page.mdx rename to content/docs/build/create-tools/improve/types-of-tools.mdx index bf3270e5e..c5d44a79d 100644 --- a/app/en/build/create-tools/improve/types-of-tools/page.mdx +++ b/content/docs/build/create-tools/improve/types-of-tools.mdx @@ -1,16 +1,14 @@ --- -title: "Types of Tools" -description: "Learn about Optimized and Unoptimized tools" +title: Types of Tools +description: Learn about Optimized and Unoptimized tools --- -# Types of Tools - Arcade offers two types of tools: - Unoptimized tools - Optimized tools -The distinction is merely a matter of how Arcade designs them. Both types of tools can be used seamlessly in the same way. No difference exists in their interfaces, the way they are called, or how you interact with them through the Arcade [Dashboard](https://api.arcade.dev/dashboard/) or the Arcade [SDK clients](/references). +The distinction is merely a matter of how Arcade designs them. Both types of tools can be used seamlessly in the same way. No difference exists in their interfaces, the way they are called, or how you interact with them through the Arcade [Dashboard](https://api.arcade.dev/dashboard/) or the Arcade [SDK clients](/en/references). Before we understand the two types, let's first understand the background for why we need to differentiate between them. @@ -44,7 +42,7 @@ Even the most powerful LLMs usually perform poorly when they need to reason such Arcade's Optimized MCP Servers are designed to match the typical data models expected in AI-powered chat interfaces and are subject to evaluation suites to ensure LLMs can safely use them. -Following the example above, our Slack MCP Server offers the [`Slack.SendMessage`](/resources/integrations/social/slack#slacksendmessage) tool, which accepts a `username` as argument, matching exactly both the action and argument value expected to be present in the LLM context window. +Following the example above, our Slack MCP Server offers the [`Slack.SendMessage`](/en/resources/integrations/social/slack#slacksendmessage) tool, which accepts a `username` as argument, matching exactly both the action and argument value expected to be present in the LLM context window. When a user says "Send a DM to John asking about a project update", the LLM can directly call the `Slack.SendMessage` tool with the `username` argument, and the tool will take care of the rest. diff --git a/app/en/build/create-tools/page.mdx b/content/docs/build/create-tools/index.mdx similarity index 54% rename from app/en/build/create-tools/page.mdx rename to content/docs/build/create-tools/index.mdx index 9a51a5586..3bac8ea81 100644 --- a/app/en/build/create-tools/page.mdx +++ b/content/docs/build/create-tools/index.mdx @@ -1,18 +1,14 @@ --- -title: "Create tools" -description: "Build, evaluate, secure, and improve custom MCP servers and tools with Arcade" +title: Create tools +description: Build, evaluate, secure, and improve custom MCP servers and tools with Arcade --- -import { Callout, Cards } from "nextra/components"; - -# Create tools - These guides are for **agent developers** who build custom MCP servers and tools with Arcade. <Callout type="info"> These guides cover *writing* new tools. If the MCP server already exists and is reachable over HTTP — yours, a vendor's, or a partner's — register it as a - [remote MCP server](/operate/governance/remote-mcp-servers) instead of + [remote MCP server](/en/operate/governance/remote-mcp-servers) instead of rebuilding its tools. </Callout> @@ -21,26 +17,26 @@ These guides are for **agent developers** who build custom MCP servers and tools <Cards num={2}> <Cards.Card title="Build a tool" - href="/build/create-tools/tool-basics" + href="/en/build/create-tools/tool-basics" /> <Cards.Card title="Evaluate tools" - href="/build/create-tools/evaluate-tools" + href="/en/build/create-tools/evaluate-tools" /> <Cards.Card title="Handle errors" - href="/build/create-tools/error-handling" + href="/en/build/create-tools/error-handling" /> <Cards.Card title="Secure your server" - href="/build/create-tools/secure-your-server" + href="/en/build/create-tools/secure-your-server" /> <Cards.Card title="Migrate from toolkits" - href="/build/create-tools/migrate-toolkits" + href="/en/build/create-tools/migrate-toolkits" /> <Cards.Card title="Types of tools" - href="/build/create-tools/improve/types-of-tools" + href="/en/build/create-tools/improve/types-of-tools" /> </Cards> diff --git a/content/docs/build/create-tools/meta.json b/content/docs/build/create-tools/meta.json new file mode 100644 index 000000000..9f609155f --- /dev/null +++ b/content/docs/build/create-tools/meta.json @@ -0,0 +1,11 @@ +{ + "title": "Create tools", + "pages": [ + "tool-basics", + "evaluate-tools", + "improve", + "error-handling", + "secure-your-server", + "migrate-toolkits" + ] +} diff --git a/app/en/build/create-tools/migrate-toolkits/page.mdx b/content/docs/build/create-tools/migrate-toolkits.mdx similarity index 94% rename from app/en/build/create-tools/migrate-toolkits/page.mdx rename to content/docs/build/create-tools/migrate-toolkits.mdx index 9207dc503..129bbf15a 100644 --- a/app/en/build/create-tools/migrate-toolkits/page.mdx +++ b/content/docs/build/create-tools/migrate-toolkits.mdx @@ -1,22 +1,18 @@ --- -title: "Migrate from toolkits to MCP servers" -description: "Learn how to migrate your existing Arcade toolkit to the new MCP Server framework" +title: Migrate from toolkits to MCP servers +description: Learn how to migrate your existing Arcade toolkit to the new MCP Server framework --- -import { Steps, Callout } from "nextra/components"; - -# Migrate from toolkits to MCP servers - This guide helps you migrate your existing Arcade toolkit to the new MCP Server framework. The `arcade-tdk` package has been deprecated in favor of `arcade-mcp-server`, and the `arcade-ai` CLI has been replaced by `arcade-mcp`. <Callout type="info"> If you're building a new MCP server from scratch, check out the [Create an MCP - Server](/build/create-tools/tool-basics/build-mcp-server) guide instead. + Server](/en/build/create-tools/tool-basics/build-mcp-server) guide instead. </Callout> <Callout type="info"> If you're migrating an existing toolkit to a new MCP server, it may be useful to read through our quickstart guide to get a sense of the new MCP Server - framework: [Create an MCP Server](/build/create-tools/tool-basics/build-mcp-server) + framework: [Create an MCP Server](/en/build/create-tools/tool-basics/build-mcp-server) </Callout> ## Understanding the changes @@ -298,4 +294,4 @@ After migrating your toolkit to an MCP server: - **Test your server**: Run your server locally and verify all tools work correctly - **Update your CI/CD**: Update any automated workflows to use the new CLI and commands - **Deploy your server**: Use `arcade deploy` to deploy your MCP server -- **Configure MCP clients**: Connect your server to [MCP clients](/build/create-tools/tool-basics/call-tools-mcp) like Claude Desktop, Cursor, or VS Code +- **Configure MCP clients**: Connect your server to [MCP clients](/en/build/create-tools/tool-basics/call-tools-mcp) like Claude Desktop, Cursor, or VS Code diff --git a/app/en/build/create-tools/secure-your-server/page.mdx b/content/docs/build/create-tools/secure-your-server/index.mdx similarity index 93% rename from app/en/build/create-tools/secure-your-server/page.mdx rename to content/docs/build/create-tools/secure-your-server/index.mdx index 14ebed43d..4a1ea9978 100644 --- a/app/en/build/create-tools/secure-your-server/page.mdx +++ b/content/docs/build/create-tools/secure-your-server/index.mdx @@ -1,4 +1,7 @@ -# Securing Arcade MCP Deployments +--- +title: Securing Arcade MCP Deployments +sidebarTitle: Overview +--- You may have noticed that when you connected to the MCP server you created with `arcade-mcp`, you could immediately call your tools from local MCP Clients and agents, like Claude and Cursor. This is because the `arcade-mcp` server is *not* secured by any mechanism by default. Most use-cases for MCP servers today are local development or local to a single machine, and we optimize for that use-case. @@ -9,7 +12,7 @@ When you `arcade deploy` your MCP server, it will be secured behind the Arcade p Under the hood, we disable the MCP routes provided by `arcade-mcp`, and use the Arcade Engine as a gateway for your MCP server, which has a number of additional features. Arcade will create a randomized secure secret for your MCP server (via the `ARCADE_WORKER_SECRET` environment variable) so that your server is protected from unauthorized access, as well as being isolated from direct access from outside of the Arcade platform. Servers managed by Arcade (servers that are `arcade deploy`ed) serve `/worker` endpoints that are protected by this secret. The worker endpoints are `worker/health`, `/worker/tools`, and `/worker/tools/invoke`. The health endpoint is not protected by this secret, but the listing tools and tool invocations are. You can explore this behavior locally by setting the same environment variable in your local environment. -Learn more about how to deploy your MCP server to Arcade [here](/build/arcade-deploy). +Learn more about how to deploy your MCP server to Arcade [here](/en/build/arcade-deploy). ## OAuth Resource Server Auth @@ -21,4 +24,4 @@ This approach is ideal when: Resource Server auth works alongside tool-level authorization. Resource Server auth secures access to the MCP server itself, while tool-level auth enables your tools to access third-party APIs on behalf of the authenticated user. -Learn more about adding front-door OAuth to your MCP server [here](/build/create-tools/secure-your-server/secure-your-mcp-server). +Learn more about adding front-door OAuth to your MCP server [here](/en/build/create-tools/secure-your-server/secure-your-mcp-server). diff --git a/content/docs/build/create-tools/secure-your-server/meta.json b/content/docs/build/create-tools/secure-your-server/meta.json new file mode 100644 index 000000000..15be892d8 --- /dev/null +++ b/content/docs/build/create-tools/secure-your-server/meta.json @@ -0,0 +1,7 @@ +{ + "title": "Secure your server", + "pages": [ + "index", + "secure-your-mcp-server" + ] +} diff --git a/app/en/build/create-tools/secure-your-server/secure-your-mcp-server/page.mdx b/content/docs/build/create-tools/secure-your-server/secure-your-mcp-server.mdx similarity index 94% rename from app/en/build/create-tools/secure-your-server/secure-your-mcp-server/page.mdx rename to content/docs/build/create-tools/secure-your-server/secure-your-mcp-server.mdx index bfadf592f..e04819154 100644 --- a/app/en/build/create-tools/secure-your-server/secure-your-mcp-server/page.mdx +++ b/content/docs/build/create-tools/secure-your-server/secure-your-mcp-server.mdx @@ -1,16 +1,14 @@ --- -title: "Adding Resource Server Auth" -description: "Secure your HTTP MCP server with OAuth 2.1 Resource Server auth" +title: Adding Resource Server Auth to Your MCP Server +metaTitle: Adding Resource Server Auth +sidebarTitle: Add Resource Server auth +description: Secure your HTTP MCP server with OAuth 2.1 Resource Server auth --- -import { Steps, Tabs, Callout } from "nextra/components"; - -# Adding Resource Server Auth to Your MCP Server - You've built an MCP server with tools that require authorization or secrets. Now you want to deploy it over HTTP so others can use it. But how do you secure it so only authorized users can access your tools? <Callout type="info"> -**Want Arcade to handle this for you?** Use [`arcade deploy`](/build/arcade-deploy) to deploy your MCP server to Arcade. We'll secure it automatically with no OAuth configuration on your end required. This guide is for self-hosted deployments where you manage your own authorization server. +**Want Arcade to handle this for you?** Use [`arcade deploy`](/en/build/arcade-deploy) to deploy your MCP server to Arcade. We'll secure it automatically with no OAuth configuration on your end required. This guide is for self-hosted deployments where you manage your own authorization server. </Callout> Resource Server auth enables your HTTP MCP server to act as an OAuth 2.1 Protected Resource (compliant with [MCP's specification for Authorization](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization)), validating Bearer tokens on every request. This unlocks support for tool-level authorization and secrets on HTTP servers, allowing you to host secure MCP servers anywhere (local, on-premise, or third-party hosted). @@ -24,7 +22,7 @@ Add [MCP compliant OAuth 2.1 front-door authentication](https://modelcontextprot <GuideOverview.Prerequisites> -- An existing MCP server created with `arcade new` (see [Create an MCP Server](/build/create-tools/tool-basics/build-mcp-server)) +- An existing MCP server created with `arcade new` (see [Create an MCP Server](/en/build/create-tools/tool-basics/build-mcp-server)) - Understanding of [MCP Authorization](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization) - An OAuth 2.1 compliant authorization server (e.g., WorkOS AuthKit, Auth0, Descope, etc.) - Authorization server's JWKS endpoint URL @@ -109,7 +107,7 @@ Update your `server.py` to add the `auth` parameter to `MCPApp`: <Tabs items={["ResourceServerAuth", "Multiple Auth Servers", "Environment Variables", "JWKSTokenValidator"]}> <Tabs.Tab> -```python filename="server.py" {5-9,11-26,32} showLineNumbers +```python title="server.py" {5-9,11-26,32} lineNumbers #!/usr/bin/env python3 """my_server MCP server""" @@ -155,7 +153,7 @@ if __name__ == "__main__": </Tabs.Tab> <Tabs.Tab> -```python filename="server.py" {10-29} showLineNumbers +```python title="server.py" {10-29} lineNumbers #!/usr/bin/env python3 """my_server MCP server""" @@ -199,7 +197,7 @@ Multiple authorization servers enable scenarios like: </Tabs.Tab> <Tabs.Tab> -```python filename="server.py" {5,8-9} showLineNumbers +```python title="server.py" {5,8-9} lineNumbers #!/usr/bin/env python3 """my_server MCP server""" @@ -215,7 +213,7 @@ app = MCPApp(name="my_server", version="1.0.0", auth=resource_server_auth) Create a `.env` file: -```bash filename=".env" +```bash title=".env" MCP_SERVER_AUTH_CANONICAL_URL=http://127.0.0.1:8000/mcp MCP_SERVER_AUTH_AUTHORIZATION_SERVERS='[ { @@ -235,7 +233,7 @@ Environment variable configuration is **recommended for production** as it separ </Tabs.Tab> <Tabs.Tab> -```python filename="server.py" {5,7-12,15} showLineNumbers +```python title="server.py" {5,7-12,15} lineNumbers #!/usr/bin/env python3 """my_server MCP server""" @@ -480,6 +478,6 @@ AuthorizationServerEntry( ## Next Steps -- **Let Arcade secure your server instead**: [Learn about `arcade deploy`](/build/arcade-deploy) -- **Build tools with authorization**: [Create tools that use OAuth](/build/create-tools/tool-basics/create-tool-auth) -- **Use secrets securely**: [Create tools with secrets](/build/create-tools/tool-basics/create-tool-secrets) +- **Let Arcade secure your server instead**: [Learn about `arcade deploy`](/en/build/arcade-deploy) +- **Build tools with authorization**: [Create tools that use OAuth](/en/build/create-tools/tool-basics/create-tool-auth) +- **Use secrets securely**: [Create tools with secrets](/en/build/create-tools/tool-basics/create-tool-secrets) diff --git a/app/en/build/create-tools/tool-basics/add-tool-metadata/page.mdx b/content/docs/build/create-tools/tool-basics/add-tool-metadata.mdx similarity index 93% rename from app/en/build/create-tools/tool-basics/add-tool-metadata/page.mdx rename to content/docs/build/create-tools/tool-basics/add-tool-metadata.mdx index 8e72c666d..d0007dfa4 100644 --- a/app/en/build/create-tools/tool-basics/add-tool-metadata/page.mdx +++ b/content/docs/build/create-tools/tool-basics/add-tool-metadata.mdx @@ -1,12 +1,8 @@ --- -title: "Add metadata to your tools" -description: "Learn how to annotate your MCP tools with ToolMetadata so that MCP clients, policy engines, and tool-selection systems understand what each tool does." +title: Add metadata to your tools +description: Learn how to annotate your MCP tools with ToolMetadata so that MCP clients, policy engines, and tool-selection systems understand what each tool does. --- -import { Steps, Tabs, Callout } from "nextra/components"; - -# Add metadata to your tools - <GuideOverview> <GuideOverview.Outcomes> @@ -16,7 +12,7 @@ Annotate your tools with structured metadata so that MCP clients, policy engines <GuideOverview.Prerequisites> -- [An MCP Server](/build/create-tools/tool-basics/build-mcp-server) +- [An MCP Server](/en/build/create-tools/tool-basics/build-mcp-server) </GuideOverview.Prerequisites> @@ -52,7 +48,7 @@ Three systems consume this metadata: Add the following import to the top of your file: -```python showLineNumbers filename="server.py" +```python lineNumbers title="server.py" from arcade_mcp_server.metadata import ( Behavior, Classification, @@ -66,7 +62,7 @@ from arcade_mcp_server.metadata import ( Pass a `ToolMetadata` instance to the `metadata` parameter of `@app.tool`. Here is a mutating tool that sends a message in Slack: -```python showLineNumbers filename="server.py" {2-12} +```python lineNumbers title="server.py" {2-12} @app.tool( metadata=ToolMetadata( classification=Classification( @@ -230,6 +226,6 @@ Only set `strict=False` when you understand and accept the apparent contradictio ## Next steps -- [Organize your MCP server and tools](/build/create-tools/tool-basics/organize-mcp-tools) -- Structure your project as it grows -- [Evaluate your tools](/build/create-tools/evaluate-tools) -- Test tool reliability and performance -- [Handle errors](/build/create-tools/error-handling) -- Return useful errors from your tools +- [Organize your MCP server and tools](/en/build/create-tools/tool-basics/organize-mcp-tools) -- Structure your project as it grows +- [Evaluate your tools](/en/build/create-tools/evaluate-tools) -- Test tool reliability and performance +- [Handle errors](/en/build/create-tools/error-handling) -- Return useful errors from your tools diff --git a/app/en/build/create-tools/tool-basics/build-mcp-server/page.mdx b/content/docs/build/create-tools/tool-basics/build-mcp-server.mdx similarity index 92% rename from app/en/build/create-tools/tool-basics/build-mcp-server/page.mdx rename to content/docs/build/create-tools/tool-basics/build-mcp-server.mdx index 21675c891..9d8c8e807 100644 --- a/app/en/build/create-tools/tool-basics/build-mcp-server/page.mdx +++ b/content/docs/build/create-tools/tool-basics/build-mcp-server.mdx @@ -1,12 +1,9 @@ --- -title: "Creating an MCP Server with Arcade" -description: "Learn how to create, test, deploy, and publish a custom MCP Server with Arcade" +title: Creating an MCP Server with Arcade +sidebarTitle: Build an MCP Server to write custom tools +description: Learn how to create, test, deploy, and publish a custom MCP Server with Arcade --- -import { Steps, Tabs, Callout } from "nextra/components"; - -# Creating an MCP Server with Arcade - The `arcade_mcp_server` package is the secure framework to build and run MCP servers with your Arcade tools. It is easiest to use with the `arcade-mcp` package (Arcade's CLI) which can scaffold your MCP server with all the necessary files and dependencies, configure MCP Clients to connect to your server, deploy your server to the cloud, and more. This guide walks you through the complete process of creating a custom MCP server with Arcade. <GuideOverview> @@ -24,7 +21,7 @@ Build and run a secure MCP server with tools that you define. <GuideOverview.YouWillLearn> -- How to run MCP servers with Arcade tools using the [`arcade_mcp_server`](/references/mcp/python) package +- How to run MCP servers with Arcade tools using the [`arcade_mcp_server`](/en/references/mcp/python) package - How to use `arcade new` from the `arcade-mcp` CLI to create your server project with all necessary files and dependencies. - How to run your local MCP Server with the Arcade CLI and register it with the Arcade Engine so that your agent can find and use your tool. @@ -88,7 +85,7 @@ my_server/ 1. **pyproject.toml** Dependencies and project configuration 1. **.env.example** Example `.env` file at the project root containing a secret required by one of the generated tools in `server.py`. Arcade automatically discovers `.env` files by traversing upward from the current directory, so placing it at the project root makes it accessible from any subdirectory. Environments are loaded on server start, so **if you update the `.env` file, you will need to restart your server.** -```python filename="server.py" showLineNumbers +```python title="server.py" lineNumbers #!/usr/bin/env python3 """my_server MCP server""" @@ -168,7 +165,7 @@ Secrets are sensitive strings like passwords, API keys, or other tokens that gra <Tabs.Tab> You can create a `.env` file at your project root directory and add your secret: -```env filename=".env" +```env title=".env" MY_SECRET_KEY="my-secret-value" ``` @@ -248,7 +245,7 @@ uv run server.py stdio a subprocess. Because of this, the server may run in a different environment and not have access to secrets defined in your local `.env` file. Please refer to the [create a tool with - secrets](/build/create-tools/tool-basics/create-tool-secrets) guide for more + secrets](/en/build/create-tools/tool-basics/create-tool-secrets) guide for more information. </Callout> @@ -268,8 +265,8 @@ For HTTP transport, view your server's API docs at [http://127.0.0.1:8000/docs]( API key and secrets in your MCP connection settings. Otherwise, if you intend to expose your HTTP MCP server to the public internet with tool-level authorization and secrets, please follow the [deploying to the cloud with - Arcade Deploy](/build/arcade-deploy) guide or the [on-prem MCP - server](/operate/deploy/on-prem) guide for secure remote deployment. + Arcade Deploy](/en/build/arcade-deploy) guide or the [on-prem MCP + server](/en/operate/deploy/on-prem) guide for secure remote deployment. </Callout> </Tabs.Tab> @@ -342,8 +339,8 @@ That's it! Your MCP server is running and connected to your AI assistant. ### Next steps -- **Create custom tools that use auth**: [Learn how to create tools with authorization](/build/create-tools/tool-basics/create-tool-auth) -- **Create custom tools that use secrets**: [Learn how to create tools with secrets](/build/create-tools/tool-basics/create-tool-secrets) -- **Learn the capabilities of the `Context` object**: [Understanding the Context object](/build/create-tools/tool-basics/runtime-data-access) -- **Evaluate your tools**: [Explore how to evaluate tool performance](/build/create-tools/evaluate-tools/why-evaluate) -- **Deploy your MCP server**: [Learn how to deploy your MCP server](/build/arcade-deploy) +- **Create custom tools that use auth**: [Learn how to create tools with authorization](/en/build/create-tools/tool-basics/create-tool-auth) +- **Create custom tools that use secrets**: [Learn how to create tools with secrets](/en/build/create-tools/tool-basics/create-tool-secrets) +- **Learn the capabilities of the `Context` object**: [Understanding the Context object](/en/build/create-tools/tool-basics/runtime-data-access) +- **Evaluate your tools**: [Explore how to evaluate tool performance](/en/build/create-tools/evaluate-tools/why-evaluate) +- **Deploy your MCP server**: [Learn how to deploy your MCP server](/en/build/arcade-deploy) diff --git a/app/en/build/create-tools/tool-basics/call-tools-mcp/page.mdx b/content/docs/build/create-tools/tool-basics/call-tools-mcp.mdx similarity index 96% rename from app/en/build/create-tools/tool-basics/call-tools-mcp/page.mdx rename to content/docs/build/create-tools/tool-basics/call-tools-mcp.mdx index 2895aba58..c83afc8c9 100644 --- a/app/en/build/create-tools/tool-basics/call-tools-mcp/page.mdx +++ b/content/docs/build/create-tools/tool-basics/call-tools-mcp.mdx @@ -1,12 +1,8 @@ --- -title: "Call tools from MCP clients" -description: "Learn how to call tools from MCP clients" +title: Call tools from MCP clients +description: Learn how to call tools from MCP clients --- -import { Steps, Tabs, Callout } from "nextra/components"; - -# Call tools from MCP clients - <GuideOverview> <GuideOverview.Outcomes> @@ -17,8 +13,8 @@ Configure your MCP clients to call tools from your MCP server. <GuideOverview.Prerequisites> - <DashboardLink path="register" /> -- [Arcade CLI](/references/arcade-cli) -- [An MCP Server](/build/create-tools/tool-basics/build-mcp-server) +- [Arcade CLI](/en/references/arcade-cli) +- [An MCP Server](/en/build/create-tools/tool-basics/build-mcp-server) - [uv package manager](https://docs.astral.sh/uv/getting-started/installation/) </GuideOverview.Prerequisites> @@ -329,7 +325,7 @@ Then, your MCP client's configuration file should look like this: should use the stdio transport and configure the Arcade API key and secrets in your MCP connection settings. If you intend to expose your HTTP MCP server to the public internet, please follow the [on-prem MCP - server](/operate/deploy/on-prem) guide for secure remote deployment. + server](/en/operate/deploy/on-prem) guide for secure remote deployment. </Callout> </Tabs.Tab> diff --git a/app/en/build/create-tools/tool-basics/compare-server-types/page.mdx b/content/docs/build/create-tools/tool-basics/compare-server-types.mdx similarity index 50% rename from app/en/build/create-tools/tool-basics/compare-server-types/page.mdx rename to content/docs/build/create-tools/tool-basics/compare-server-types.mdx index bcae4e934..c80e979ba 100644 --- a/app/en/build/create-tools/tool-basics/compare-server-types/page.mdx +++ b/content/docs/build/create-tools/tool-basics/compare-server-types.mdx @@ -1,17 +1,17 @@ --- -title: "Compare Server Types" -description: "Compare the different types of MCP servers" +title: Compare MCP Server Types +metaTitle: Compare Server Types +sidebarTitle: Compare MCP server types +description: Compare the different types of MCP servers --- -# Compare MCP Server Types - Depending on the transport you use and where you want to run your MCP server, Arcade offers different functionalities and features. Below is a comparison of the different server types and their capabilities. | Transport | Deployment | Tools without requirements | Tools with secrets | Tools with auth (single-user) | Tools with auth (multi-user) | | --------- | ---------------------------------------------------------------------------- | :------------------------: | :----------------: | :---------------------------: | :--------------------------: | | stdio | local | ✅ | ✅ | ✅ | ❌ | -| http | local ([unprotected](/resources/glossary#unprotected-mcp-servers)) | ✅ | ❌ | ❌ | ❌ | -| http | remote ([unprotected](/resources/glossary#unprotected-mcp-servers)) | ✅ | ❌ | ❌ | ❌ | -| http | local ([protected](/resources/glossary#protected-mcp-servers)) | ✅ | ✅ | ✅ | ✅ | -| http | remote ([protected](/resources/glossary#protected-mcp-servers)) | ✅ | ✅ | ✅ | ✅ | +| http | local ([unprotected](/en/resources/glossary#unprotected-mcp-servers)) | ✅ | ❌ | ❌ | ❌ | +| http | remote ([unprotected](/en/resources/glossary#unprotected-mcp-servers)) | ✅ | ❌ | ❌ | ❌ | +| http | local ([protected](/en/resources/glossary#protected-mcp-servers)) | ✅ | ✅ | ✅ | ✅ | +| http | remote ([protected](/en/resources/glossary#protected-mcp-servers)) | ✅ | ✅ | ✅ | ✅ | | http | Arcade Cloud | ✅ | ✅ | ✅ | ✅ | diff --git a/app/en/build/create-tools/tool-basics/create-tool-auth/page.mdx b/content/docs/build/create-tools/tool-basics/create-tool-auth.mdx similarity index 89% rename from app/en/build/create-tools/tool-basics/create-tool-auth/page.mdx rename to content/docs/build/create-tools/tool-basics/create-tool-auth.mdx index a3ffbb1d4..00655c368 100644 --- a/app/en/build/create-tools/tool-basics/create-tool-auth/page.mdx +++ b/content/docs/build/create-tools/tool-basics/create-tool-auth.mdx @@ -1,12 +1,9 @@ --- -title: "Add user authorization to your MCP tools" -description: "Learn how to build custom MCP tools that require user authorization using Arcade, auth providers, and OAuth." +title: Add user authorization to your MCP tools +sidebarTitle: Create a tool with auth +description: Learn how to build custom MCP tools that require user authorization using Arcade, auth providers, and OAuth. --- -import { Steps, Tabs, Callout } from "nextra/components"; - -# Add user authorization to your MCP tools - <GuideOverview> <GuideOverview.Outcomes> @@ -18,7 +15,7 @@ Create and use an MCP tool that requires OAuth to access Reddit, prompting users - <DashboardLink path="register" /> - [uv package manager](https://docs.astral.sh/uv/getting-started/installation/) -- [Create an MCP Server](/build/create-tools/tool-basics/build-mcp-server) +- [Create an MCP Server](/en/build/create-tools/tool-basics/build-mcp-server) </GuideOverview.Prerequisites> @@ -42,7 +39,7 @@ When you create a tool with `requires_auth`, you specify which provider to use. 2. If the tool's requirements are not met, Arcade initiates the provider-specific OAuth flow for the requested scopes. > 2a). The user is presented with a URL to complete the OAuth challenge. The user will need to visit this URL and log in and explicitly grant consent for the action to be performed on their behalf. This is the "OAuth challenge". - > 2b). The provider issues the token, and Arcade will securely inject it into the tool's [`Context`](/build/create-tools/tool-basics/runtime-data-access) on its next invocation. The client and the LLM will never see the token. + > 2b). The provider issues the token, and Arcade will securely inject it into the tool's [`Context`](/en/build/create-tools/tool-basics/runtime-data-access) on its next invocation. The client and the LLM will never see the token. > 2c). The tool needs to be re-invoked - this time its requirements will be met. 3. The tool is executed, and uses the token injected into its `Context` to call the provider's API (e.g., `https://oauth.reddit.com`), without the LLM or client ever seeing the token. @@ -57,7 +54,7 @@ The auth provider defines where the identity lives, what permissions are availab Create a new Python file, e.g., `auth_tools.py`, and import the necessary modules and classes: -```python showLineNumbers filename="auth_tools.py" +```python lineNumbers title="auth_tools.py" import sys from typing import Annotated @@ -70,17 +67,17 @@ from arcade_mcp_server.auth import Reddit Create an instance of the `MCPApp` class: -```python filename="auth_tools.py" +```python title="auth_tools.py" app = MCPApp(name="auth_example", version="1.0.0", log_level="DEBUG") ``` ### Define your MCP tool -Now, define your tool using the `@app.tool` decorator and specify the required authorization, in this case, by using [Arcade's Reddit auth provider](/references/auth-providers/reddit). +Now, define your tool using the `@app.tool` decorator and specify the required authorization, in this case, by using [Arcade's Reddit auth provider](/en/references/auth-providers/reddit). Specifying the `requires_auth` parameter in the `@app.tool` decorator indicates that the tool needs user authorization. In this example, we're using the `Reddit` auth provider with the `read` scope: -```python showLineNumbers filename="auth_tools.py" {1-5} {16} +```python lineNumbers title="auth_tools.py" {1-5} {16} @app.tool( requires_auth=Reddit( scopes=["read"] @@ -124,7 +121,7 @@ arcade login </Callout> -Arcade offers a number of [built-in auth providers](/references/auth-providers), including Slack, Google, and GitHub. You can also require authorization with a custom auth provider, using the `OAuth2` class, a subclass of the `ToolAuthorization` class: +Arcade offers a number of [built-in auth providers](/en/references/auth-providers), including Slack, Google, and GitHub. You can also require authorization with a custom auth provider, using the `OAuth2` class, a subclass of the `ToolAuthorization` class: ```python @app.tool( @@ -147,7 +144,7 @@ Arcade offers a number of [built-in auth providers](/references/auth-providers), Specify the OAuth scopes you need for your tool. In this example, you already are using the `read` scope, but you can specify multiple scopes for more permissions (like `identity`): -```python showLineNumbers filename="auth_tools.py" {2} +```python lineNumbers title="auth_tools.py" {2} # Multiple scopes for more permissions @app.tool(requires_auth=Reddit(scopes=["read", "identity"])) async def identity_tool(context: Context) -> dict: @@ -161,7 +158,7 @@ Scopes are defined by the auth provider, and they vary between providers. Each s ### Run the MCP Server -```python showLineNumbers filename="auth_tools.py" +```python lineNumbers title="auth_tools.py" if __name__ == "__main__": # Get transport from command line argument, default to "stdio" transport = sys.argv[1] if len(sys.argv) > 1 else "stdio" @@ -230,7 +227,7 @@ Arcade manages the OAuth flow, and provides the token via `context.get_auth_toke To get the authorization token, use the `context.get_auth_token_or_empty()` method. -```python filename="auth_tools.py" +```python title="auth_tools.py" # Get the token (returns empty string if not authenticated) oauth_token = context.get_auth_token_or_empty() @@ -245,7 +242,7 @@ headers = { Use the OAuth token with httpx or other HTTP clients: -```python filename="auth_tools.py" +```python title="auth_tools.py" import httpx async with httpx.AsyncClient() as client: @@ -274,11 +271,11 @@ async with httpx.AsyncClient() as client: - Try adding more authorized tools - Explore how to handle different authorization providers and scopes -- Learn how to [build a tool with secrets](/build/create-tools/tool-basics/create-tool-secrets) +- Learn how to [build a tool with secrets](/en/build/create-tools/tool-basics/create-tool-secrets) ## Example Code -```python showLineNumbers filename="auth_tools.py" +```python lineNumbers title="auth_tools.py" #!/usr/bin/env python3 import sys from typing import Annotated diff --git a/app/en/build/create-tools/tool-basics/create-tool-secrets/page.mdx b/content/docs/build/create-tools/tool-basics/create-tool-secrets.mdx similarity index 92% rename from app/en/build/create-tools/tool-basics/create-tool-secrets/page.mdx rename to content/docs/build/create-tools/tool-basics/create-tool-secrets.mdx index e44d22988..135d05f41 100644 --- a/app/en/build/create-tools/tool-basics/create-tool-secrets/page.mdx +++ b/content/docs/build/create-tools/tool-basics/create-tool-secrets.mdx @@ -1,12 +1,9 @@ --- -title: "Create an MCP tool with secrets" -description: "Learn how to build custom MCP tools that require secrets using Arcade" +title: Create an MCP tool with secrets +sidebarTitle: Create a tool with secrets +description: Learn how to build custom MCP tools that require secrets using Arcade --- -import { Steps, Tabs, Callout } from "nextra/components"; - -# Create an MCP tool with secrets - <GuideOverview> <GuideOverview.Outcomes> @@ -17,8 +14,8 @@ Build an MCP tool that can read a secret from Context and return a masked confir <GuideOverview.Prerequisites> - <DashboardLink path="register" /> -- [Arcade CLI](/get-started/quickstarts/call-tool-agent) -- [An MCP Server](/build/create-tools/tool-basics/build-mcp-server) +- [Arcade CLI](/en/get-started/quickstarts/call-tool-agent) +- [An MCP Server](/en/build/create-tools/tool-basics/build-mcp-server) - [uv package manager](https://docs.astral.sh/uv/getting-started/installation/) </GuideOverview.Prerequisites> @@ -70,7 +67,7 @@ Depending on where you're running your server, you can store your secret in a fe <Tabs.Tab> You can create a `.env` file in your project root directory and add your secret: -```env filename=".env" +```env title=".env" MY_SECRET_KEY="my-secret-value" ``` @@ -160,7 +157,7 @@ $env:MY_SECRET_KEY="my-secret-value" When using the stdio transport, MCP clients typically launch the MCP server as a subprocess. Because of this, the server may run in a different environment and not have access to secrets defined in your local `.env` file or your terminal environment variables. To ensure your stdio MCP server has access to the secrets, you can either -1. Utilize the [`arcade configure` CLI command](/references/arcade-cli) to configure your MCP client to pass the secrets to your MCP server, or +1. Utilize the [`arcade configure` CLI command](/en/references/arcade-cli) to configure your MCP client to pass the secrets to your MCP server, or 2. Manually configure your MCP client to pass the secrets to your MCP server. For example, if you are using Cursor IDE, you can add the following to your `mcp.json` file: ```json @@ -195,12 +192,12 @@ Note that the specific key name may vary depending on the MCP client you are usi credentials, etc, and not to simply print a confirmation string. </Callout> -In your [MCP Server](/build/create-tools/tool-basics/build-mcp-server), create a new tool that uses the secret: +In your [MCP Server](/en/build/create-tools/tool-basics/build-mcp-server), create a new tool that uses the secret: - Use the `requires_secrets` parameter to declare which secrets your tool needs (`"SECRET_KEY"` in this example). - The tool's Context object has a `get_secret` method that you can use to access the secret value. -```python filename="secrets.py" showLineNumbers highlightLines={2,4,7} +```python title="secrets.py" lineNumbers {2,4,7} @app.tool( requires_secrets=["SECRET_KEY"], # declare we need SECRET_KEY ) @@ -241,7 +238,7 @@ When your tool is executed, it will return: `"Got SECRET_KEY of length..."`. In ### Environment Variables -```env filename=".env" +```env title=".env" SECRET_KEY="supersecret" ``` @@ -251,7 +248,7 @@ For the code to work, you must define your environment variables locally or in a </Callout> -```python filename="secrets.py" +```python title="secrets.py" #!/usr/bin/env python3 import sys @@ -312,8 +309,8 @@ For HTTP transport, view your server's API docs at [http://127.0.0.1:8000/docs]( authorization and secrets. If you need to use tool-level authorization or secrets locally, you should use the stdio transport and configure the Arcade API key and secrets in your MCP connection settings. Otherwise, if you intend to expose your HTTP MCP server to - the public internet with tool-level authorization and secrets, please follow the [deploying to the cloud with Arcade Deploy](/build/arcade-deploy) guide or the [on-prem MCP - server](/operate/deploy/on-prem) guide for secure remote deployment. + the public internet with tool-level authorization and secrets, please follow the [deploying to the cloud with Arcade Deploy](/en/build/arcade-deploy) guide or the [on-prem MCP + server](/en/operate/deploy/on-prem) guide for secure remote deployment. </Callout> </Tabs.Tab> diff --git a/app/en/build/create-tools/tool-basics/page.mdx b/content/docs/build/create-tools/tool-basics/index.mdx similarity index 58% rename from app/en/build/create-tools/tool-basics/page.mdx rename to content/docs/build/create-tools/tool-basics/index.mdx index 50e5153ae..690720696 100644 --- a/app/en/build/create-tools/tool-basics/page.mdx +++ b/content/docs/build/create-tools/tool-basics/index.mdx @@ -1,15 +1,13 @@ --- -title: "Build a Tool" -description: "Learn the fundamentals of building tools with Arcade's MCP Server framework" +title: Build a Tool +sidebarTitle: Overview +description: Learn the fundamentals of building tools with Arcade's MCP Server framework --- -import { SubpageList } from '../../../../_components/subpage-list'; -import meta from './_meta'; - -# Build a Tool +import { SubpageList } from '@/components/subpage-list'; Learn how to create custom tools that extend AI agents with powerful capabilities. Start here when you're ready to build your first tool or need to add new functionality to your existing MCP servers. Building tools with Arcade allows you to extend AI agents with custom capabilities. This section covers everything you need to know about creating powerful, reusable tools using the Model Context Protocol (MCP). -<SubpageList basePath="/build/create-tools/tool-basics" meta={meta} /> +<SubpageList basePath="/build/create-tools/tool-basics" /> diff --git a/content/docs/build/create-tools/tool-basics/meta.json b/content/docs/build/create-tools/tool-basics/meta.json new file mode 100644 index 000000000..c8858a27e --- /dev/null +++ b/content/docs/build/create-tools/tool-basics/meta.json @@ -0,0 +1,14 @@ +{ + "title": "Build a tool", + "pages": [ + "index", + "compare-server-types", + "build-mcp-server", + "create-tool-auth", + "create-tool-secrets", + "runtime-data-access", + "call-tools-mcp", + "organize-mcp-tools", + "add-tool-metadata" + ] +} diff --git a/app/en/build/create-tools/tool-basics/organize-mcp-tools/page.mdx b/content/docs/build/create-tools/tool-basics/organize-mcp-tools.mdx similarity index 91% rename from app/en/build/create-tools/tool-basics/organize-mcp-tools/page.mdx rename to content/docs/build/create-tools/tool-basics/organize-mcp-tools.mdx index 6f853bcc3..bfadc40ee 100644 --- a/app/en/build/create-tools/tool-basics/organize-mcp-tools/page.mdx +++ b/content/docs/build/create-tools/tool-basics/organize-mcp-tools.mdx @@ -1,12 +1,8 @@ --- -title: "Organize your MCP server and tools" -description: "Learn best practices for organizing your MCP server and tools, how to import tools from other packages and how to use them together." +title: Organize your MCP server and tools +description: Learn best practices for organizing your MCP server and tools, how to import tools from other packages and how to use them together. --- -import { Steps, Tabs, Callout } from "nextra/components"; - -# Organize your MCP server and tools - <GuideOverview> <GuideOverview.Outcomes> @@ -17,7 +13,7 @@ Learn best practices for organizing your MCP server and tools, how to import too <GuideOverview.Prerequisites> - <DashboardLink path="register" /> -- [An MCP Server](/build/create-tools/tool-basics/build-mcp-server) +- [An MCP Server](/en/build/create-tools/tool-basics/build-mcp-server) - [uv package manager](https://docs.astral.sh/uv/getting-started/installation/) </GuideOverview.Prerequisites> @@ -54,7 +50,7 @@ We recommend keeping your MCP server file and tools in separate files in a `tool You can use the `@app.tool` decorator to define your tools in your MCP server file directly on the MCP server app object (`MCPApp`) like this: -```python filename="server.py" +```python title="server.py" @app.tool def server_info() -> Annotated[dict, "Information about this server"]: """Return information about this MCP server.""" @@ -72,28 +68,28 @@ However, if you need to define more than a few tools, this can make your MCP ser You can import tools from separate files like this: -```python filename="server.py" +```python title="server.py" from tools.math_tools import add, multiply from tools.text_tools import capitalize_string, word_count ``` You could also import specific tools from Arcade PyPI packages: -```python filename="server.py" +```python title="server.py" # This is a prebuilt Arcade server - `pip install arcade-gmail` from arcade_gmail.tools import list_emails ``` You can also import whole modules that contain tools like this: -```python filename="server.py" +```python title="server.py" # This is a prebuilt Arcade server - `pip install arcade-reddit` import arcade_reddit ``` Add imported tools explicitly to the `MCPApp` instance like this: -```python filename="server.py" +```python title="server.py" app.add_tool(add) app.add_tool(list_emails) app.add_tools_from_module(arcade_reddit) @@ -109,7 +105,7 @@ app.add_tools_from_module(arcade_reddit) ## Example Code -```python filename="server.py" +```python title="server.py" #!/usr/bin/env python3 import sys diff --git a/app/en/build/create-tools/tool-basics/runtime-data-access/page.mdx b/content/docs/build/create-tools/tool-basics/runtime-data-access.mdx similarity index 95% rename from app/en/build/create-tools/tool-basics/runtime-data-access/page.mdx rename to content/docs/build/create-tools/tool-basics/runtime-data-access.mdx index 575daf674..c337e8100 100644 --- a/app/en/build/create-tools/tool-basics/runtime-data-access/page.mdx +++ b/content/docs/build/create-tools/tool-basics/runtime-data-access.mdx @@ -1,12 +1,10 @@ --- -title: "Context and MCP tools" -description: "What Arcade's Tool Context is and how to use it." +title: Understanding `Context` and tools +metaTitle: Context and MCP tools +sidebarTitle: Access runtime data +description: What Arcade's Tool Context is and how to use it. --- -import { Callout, Tabs } from "nextra/components"; - -# Understanding `Context` and tools - The `Context` class is used by tools for both runtime capabilities and tool-specific data access. Tools receive a populated `Context` instance as a parameter. Tools should not create `Context` instances directly. <GuideOverview> @@ -139,7 +137,7 @@ For the code to work, you must define your environment variables in a `.env` fil </Callout> -```python filename="server.py" +```python title="server.py" #!/usr/bin/env python3 import sys @@ -292,5 +290,5 @@ For HTTP transport, view your server's API docs at [http://127.0.0.1:8000/docs]( ### Next Steps -- [Build a custom tool that requires user authorization](/build/create-tools/tool-basics/create-tool-auth) -- [Build a custom tool with secrets](/build/create-tools/tool-basics/create-tool-secrets) +- [Build a custom tool that requires user authorization](/en/build/create-tools/tool-basics/create-tool-auth) +- [Build a custom tool with secrets](/en/build/create-tools/tool-basics/create-tool-secrets) diff --git a/app/en/build/page.mdx b/content/docs/build/index.mdx similarity index 57% rename from app/en/build/page.mdx rename to content/docs/build/index.mdx index b1172b1c6..e53a84bb2 100644 --- a/app/en/build/page.mdx +++ b/content/docs/build/index.mdx @@ -1,29 +1,26 @@ --- -title: "Build with Arcade" +title: Build with Arcade +sidebarTitle: Overview description: "Guides for agent developers: call tools, create MCP servers, and ship user-facing agents" --- -import { Cards } from "nextra/components"; - -# Build with Arcade - These guides are for **agent developers**: people who call Arcade tools from agents, build custom MCP servers, or ship user-facing agents. -If you deploy and govern Arcade for a company, go to [Operate](/operate). +If you deploy and govern Arcade for a company, go to [Operate](/en/operate). ## Start here <Cards num={2}> <Cards.Card title="Call tools in your agent" - href="/get-started/quickstarts/call-tool-agent" + href="/en/get-started/quickstarts/call-tool-agent" /> <Cards.Card title="Build an MCP server" - href="/build/create-tools/tool-basics/build-mcp-server" + href="/en/build/create-tools/tool-basics/build-mcp-server" /> - <Cards.Card title="Call tools" href="/build/tool-calling" /> - <Cards.Card title="Create tools" href="/build/create-tools" /> + <Cards.Card title="Call tools" href="/en/build/tool-calling" /> + <Cards.Card title="Create tools" href="/en/build/create-tools" /> </Cards> ## Ship to production @@ -31,12 +28,12 @@ If you deploy and govern Arcade for a company, go to [Operate](/operate). <Cards num={2}> <Cards.Card title="Secure auth in production" - href="/build/user-facing-agents/secure-auth-production" + href="/en/build/user-facing-agents/secure-auth-production" /> <Cards.Card title="Arcade Deploy" - href="/build/arcade-deploy" + href="/en/build/arcade-deploy" /> </Cards> -Arcade Deploy hosts *your* MCP server on Arcade Cloud. To deploy the Arcade platform itself, see [Operate → Deployment](/operate/deploy). +Arcade Deploy hosts *your* MCP server on Arcade Cloud. To deploy the Arcade platform itself, see [Operate → Deployment](/en/operate/deploy). diff --git a/content/docs/build/meta.json b/content/docs/build/meta.json new file mode 100644 index 000000000..5eaf24043 --- /dev/null +++ b/content/docs/build/meta.json @@ -0,0 +1,13 @@ +{ + "title": "Build", + "icon": "Ruler", + "pages": [ + "index", + "[Quickstart: call tools in agents](/get-started/quickstarts/call-tool-agent)", + "[Quickstart: build an MCP server](/get-started/quickstarts/mcp-server-quickstart)", + "tool-calling", + "create-tools", + "user-facing-agents", + "arcade-deploy" + ] +} diff --git a/app/en/build/tool-calling/call-third-party-apis/page.mdx b/content/docs/build/tool-calling/call-third-party-apis.mdx similarity index 93% rename from app/en/build/tool-calling/call-third-party-apis/page.mdx rename to content/docs/build/tool-calling/call-third-party-apis.mdx index cbf5e92b5..2fb739e89 100644 --- a/app/en/build/tool-calling/call-third-party-apis/page.mdx +++ b/content/docs/build/tool-calling/call-third-party-apis.mdx @@ -1,12 +1,10 @@ --- -title: "Directly call third-party APIs" -description: "Guide on how to retrieve an authorization token to call third-party APIs directly" +title: Directly call third-party APIs +sidebarTitle: Call third-party APIs +description: Guide on how to retrieve an authorization token to call third-party APIs directly --- -import { Steps, Tabs, Callout } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; - -# Directly call third-party APIs +import { SignupLink } from "@/components/analytics"; In this guide, you'll learn how to use Arcade to obtain user authorization and interact with third-party services by calling their API endpoints directly, without using Arcade for tool execution or definition. We'll use Google's Gmail API as an example to demonstrate how to: @@ -21,7 +19,7 @@ This can be useful when you need to manage authorization flows in your applicati ### Prerequisites - Sign up for an <SignupLink linkLocation="docs:call-third-party-apis-directly">Arcade account</SignupLink> if you haven't already -- Generate an [Arcade API key](/get-started/setup/api-keys) and take note of it +- Generate an [Arcade API key](/en/get-started/setup/api-keys) and take note of it ### Install required libraries @@ -320,7 +318,7 @@ For each item in the list/array, you could use the [`users.messages.get`](https: </Steps> -<Callout type="info">Consider using the [Arcade Gmail MCP Server](/resources/integrations/productivity/gmail), which simplifies the process for retrieving email messages even further! The pattern described here is useful if you need to directly get a token to use with Google in other parts of your codebase.</Callout> +<Callout type="info">Consider using the [Arcade Gmail MCP Server](/en/resources/integrations/productivity/gmail), which simplifies the process for retrieving email messages even further! The pattern described here is useful if you need to directly get a token to use with Google in other parts of your codebase.</Callout> ### How it works @@ -330,4 +328,4 @@ Arcade handles the authorization challenges and tokens, simplifying the process ### Next steps -Integrate this authorization flow into your application, and explore how you can manage different [auth providers](/references/auth-providers) and scopes. +Integrate this authorization flow into your application, and explore how you can manage different [auth providers](/en/references/auth-providers) and scopes. diff --git a/app/en/build/tool-calling/custom-apps/auth-tool-calling/page.mdx b/content/docs/build/tool-calling/custom-apps/auth-tool-calling.mdx similarity index 89% rename from app/en/build/tool-calling/custom-apps/auth-tool-calling/page.mdx rename to content/docs/build/tool-calling/custom-apps/auth-tool-calling.mdx index a86d91827..b6de33ae8 100644 --- a/app/en/build/tool-calling/custom-apps/auth-tool-calling/page.mdx +++ b/content/docs/build/tool-calling/custom-apps/auth-tool-calling.mdx @@ -1,10 +1,9 @@ --- -title: "Authorized Tool Calling" -description: "Guide on calling tools that require authorization" +title: Authorized Tool Calling +sidebarTitle: Authorize tool calling +description: Guide on calling tools that require authorization --- -# Authorized Tool Calling - Arcade provides an authorization system that handles OAuth 2.0, API keys, and user tokens needed by AI agents to access external services through tools. This means your AI agents can now act on behalf of users securely and privately. With Arcade, developers can now create agents that can as _as the end user of their application_ to perform tasks like: @@ -15,8 +14,6 @@ With Arcade, developers can now create agents that can as _as the end user of th Arcade also allows for actions (tools) to be authorized directly. For example, to access a user's Gmail account, you can utilize the following guide. -import { Steps, Tabs, Callout } from "nextra/components"; - <Steps> ### Initialize the client @@ -158,6 +155,6 @@ These APIs give you programmatic control over tool calling. ### Next steps -Arcade also allows you to [build your own tools](/build/create-tools/tool-basics/build-mcp-server) to integrate any custom functionality or API to your Agent or AI workflows. +Arcade also allows you to [build your own tools](/en/build/create-tools/tool-basics/build-mcp-server) to integrate any custom functionality or API to your Agent or AI workflows. -Your tools can use the [service providers supported by Arcade](/references/auth-providers) or you can integrate with any [OAuth2-compatible service](/references/auth-providers/oauth2). +Your tools can use the [service providers supported by Arcade](/en/references/auth-providers) or you can integrate with any [OAuth2-compatible service](/en/references/auth-providers/oauth2). diff --git a/app/en/build/tool-calling/custom-apps/check-auth-status/page.mdx b/content/docs/build/tool-calling/custom-apps/check-auth-status.mdx similarity index 96% rename from app/en/build/tool-calling/custom-apps/check-auth-status/page.mdx rename to content/docs/build/tool-calling/custom-apps/check-auth-status.mdx index ba9ed864e..bef64844b 100644 --- a/app/en/build/tool-calling/custom-apps/check-auth-status/page.mdx +++ b/content/docs/build/tool-calling/custom-apps/check-auth-status.mdx @@ -1,10 +1,10 @@ --- -title: "Checking Tool Auth Status" -description: "Guide on checking the auth status of a tool" +title: Checking Tool Authorization Status +metaTitle: Checking Tool Auth Status +sidebarTitle: Check authorization status +description: Guide on checking the auth status of a tool --- -# Checking Tool Authorization Status - Before executing tools that require authorization, you can check their authorization status to understand what permissions are needed and whether they're currently available for a user. This is useful for: @@ -13,8 +13,6 @@ This is useful for: - Understanding which tools need user approval - Debugging authorization issues -import { Steps, Tabs, Callout } from "nextra/components"; - ### Initialize the client diff --git a/app/en/build/tool-calling/custom-apps/get-tool-definitions/page.mdx b/content/docs/build/tool-calling/custom-apps/get-tool-definitions.mdx similarity index 97% rename from app/en/build/tool-calling/custom-apps/get-tool-definitions/page.mdx rename to content/docs/build/tool-calling/custom-apps/get-tool-definitions.mdx index 9a231fb6b..7416f5c7f 100644 --- a/app/en/build/tool-calling/custom-apps/get-tool-definitions/page.mdx +++ b/content/docs/build/tool-calling/custom-apps/get-tool-definitions.mdx @@ -1,12 +1,10 @@ --- -title: "Get Formatted Tool Definitions" -description: "Learn how to get formatted tool definitions using Arcade" +title: Get Formatted Tool Definitions +sidebarTitle: Get formatted tool definitions +description: Learn how to get formatted tool definitions using Arcade --- -import { Tabs } from "nextra/components"; -import ToggleContent from "@/app/_components/toggle-content"; - -# Get Formatted Tool Definitions +import ToggleContent from "@/components/toggle-content"; When calling tools directly, it can be useful to get tool definitions in a specific model provider's format. The Arcade Client provides methods for getting a tool's definition and also for listing the definitions of multiple tools in a specific model provider's format. diff --git a/app/en/build/tool-calling/custom-apps/page.mdx b/content/docs/build/tool-calling/custom-apps/index.mdx similarity index 56% rename from app/en/build/tool-calling/custom-apps/page.mdx rename to content/docs/build/tool-calling/custom-apps/index.mdx index 9af1e72c1..87bd55ab8 100644 --- a/app/en/build/tool-calling/custom-apps/page.mdx +++ b/content/docs/build/tool-calling/custom-apps/index.mdx @@ -1,15 +1,13 @@ --- -title: "In Custom Applications" -description: "Learn how to integrate Arcade tools into your custom applications with authentication and tool management" +title: In Custom Applications +sidebarTitle: Overview +description: Learn how to integrate Arcade tools into your custom applications with authentication and tool management --- -import { SubpageList } from '../../../../_components/subpage-list'; -import meta from './_meta'; - -# In Custom Applications +import { SubpageList } from '@/components/subpage-list'; Learn how to integrate Arcade tools into your custom applications. Use these guides when building your own tool-calling interfaces and need to handle authentication, status checking, and tool definitions programmatically. Building custom applications with Arcade tools requires understanding how to manage user authentication, check authorization status, and retrieve properly formatted tool definitions for your specific use case. -<SubpageList basePath="/build/tool-calling/custom-apps" meta={meta} /> \ No newline at end of file +<SubpageList basePath="/build/tool-calling/custom-apps" /> \ No newline at end of file diff --git a/content/docs/build/tool-calling/custom-apps/meta.json b/content/docs/build/tool-calling/custom-apps/meta.json new file mode 100644 index 000000000..bb58198dc --- /dev/null +++ b/content/docs/build/tool-calling/custom-apps/meta.json @@ -0,0 +1,9 @@ +{ + "title": "In custom applications", + "pages": [ + "index", + "auth-tool-calling", + "check-auth-status", + "get-tool-definitions" + ] +} diff --git a/app/en/build/tool-calling/error-handling/page.mdx b/content/docs/build/tool-calling/error-handling.mdx similarity index 95% rename from app/en/build/tool-calling/error-handling/page.mdx rename to content/docs/build/tool-calling/error-handling.mdx index 241212a96..24c591f8c 100644 --- a/app/en/build/tool-calling/error-handling/page.mdx +++ b/content/docs/build/tool-calling/error-handling.mdx @@ -1,12 +1,11 @@ --- -title: "Tool Error Handling" -description: "Learn how to handle errors when using tools with Arcade's Tool Development Kit (TDK)" +title: Tool error handling +metaTitle: Tool Error Handling +sidebarTitle: Handling errors +description: Learn how to handle errors when using tools with Arcade's Tool Development Kit (TDK) --- -import { Tabs } from "nextra/components"; -import { ErrorHierarchy } from "@/app/_components/error-hierarchy"; - -# Tool error handling +import { ErrorHierarchy } from "@/components/error-hierarchy"; When calling tools from your Agent, smart error handling is crucial for creating robust and reliable applications. This guide covers everything you need to know about handling errors from a tool user's perspective. @@ -279,8 +278,8 @@ As of now, MCP Clients do not return structured error information, only an error ## Debugging a failed tool call after the fact -Arcade records every tool run, so you don't have to reproduce a failure to inspect it. Open [Tool executions](/operate/governance/tool-executions) to find the run, read each attempt's error, and reveal the exact inputs the tool received and the outputs it returned. +Arcade records every tool run, so you don't have to reproduce a failure to inspect it. Open [Tool executions](/en/operate/governance/tool-executions) to find the run, read each attempt's error, and reveal the exact inputs the tool received and the outputs it returned. ## Building tools with error handling -To learn more about how to build tools with error handling, see the [Build Tools](/build/create-tools/error-handling/useful-tool-errors) section. +To learn more about how to build tools with error handling, see the [Build Tools](/en/build/create-tools/error-handling/useful-tool-errors) section. diff --git a/app/en/build/tool-calling/page.mdx b/content/docs/build/tool-calling/index.mdx similarity index 78% rename from app/en/build/tool-calling/page.mdx rename to content/docs/build/tool-calling/index.mdx index 76c0631c5..a47dddd72 100644 --- a/app/en/build/tool-calling/page.mdx +++ b/content/docs/build/tool-calling/index.mdx @@ -1,10 +1,10 @@ --- -title: "Overview" +title: What are tools? +metaTitle: Overview +sidebarTitle: Overview description: Overview of language model tool calling and how to use tools with Arcade --- -# What are tools? - Language models excel at text generation but struggle with tasks like calculations, data retrieval, or interacting with external systems. To make an analogy, these models have impressive _brains_ or reasoning capabilities, but lack the _hands_ to interact with the digital world. To solve this, many AI models support tool calling (sometimes referred to as 'function calling'). @@ -13,11 +13,11 @@ To solve this, many AI models support tool calling (sometimes referred to as 'fu Say a colleague shares a document with you on Google Drive, and you'd like an LLM to help you analyze it. -You could go to your Drive/Docs, open the document, copy its contents, and paste it into your chat. But what if the LLM could do this for you? The Arcade Google Docs MCP Server provides a [`SearchAndRetrieveDocuments`](/resources/integrations/productivity/googledocs#googledocssearchandretrievedocuments) tool. By calling it, the LLM can find and read the document without you having to do anything. +You could go to your Drive/Docs, open the document, copy its contents, and paste it into your chat. But what if the LLM could do this for you? The Arcade Google Docs MCP Server provides a [`SearchAndRetrieveDocuments`](/en/resources/integrations/productivity/googledocs#googledocssearchandretrievedocuments) tool. By calling it, the LLM can find and read the document without you having to do anything. -After analyzing the document, you decide that a meeting is needed with your colleague. You can ask the LLM to schedule a meeting and it will use the [Google Calendar MCP Server](/resources/integrations/productivity/googlecalendar) to do it without you needing to leave the chat. +After analyzing the document, you decide that a meeting is needed with your colleague. You can ask the LLM to schedule a meeting and it will use the [Google Calendar MCP Server](/en/resources/integrations/productivity/googlecalendar) to do it without you needing to leave the chat. -Or you could ask the LLM to send a summary of the analysis to your colleague by email and it would use the [Gmail MCP Server](/resources/integrations/productivity/gmail) for that. +Or you could ask the LLM to send a summary of the analysis to your colleague by email and it would use the [Gmail MCP Server](/en/resources/integrations/productivity/gmail) for that. ## Possibilities for Application and AI Agent developers @@ -40,9 +40,9 @@ In the example above, when the user asks: "_help me analyze the 'Project XYZ' Go One challenge to make all that happen is authorization. How do you give the LLM permission to access someone's Google Docs and Gmail in a secure and convenient way? -Arcade solves this problem by providing a standardized [interface for authorization](/get-started/about-arcade), as well as pre-built auth providers for [popular services](/references/auth-providers) such as Google, Dropbox, GitHub, Notion, and many more. +Arcade solves this problem by providing a standardized [interface for authorization](/en/get-started/about-arcade), as well as pre-built auth providers for [popular services](/en/references/auth-providers) such as Google, Dropbox, GitHub, Notion, and many more. -Our SDK also [allows you to integrate](/references/auth-providers/oauth2) LLMs with any OAuth 2.0-compliant API. +Our SDK also [allows you to integrate](/en/references/auth-providers/oauth2) LLMs with any OAuth 2.0-compliant API. ## Tool Augmented Generation (TAG) @@ -93,5 +93,5 @@ This process shows the general outline of the Tool Augmented Generation (TAG) pr ### Next steps -- Explore the [MCP Servers](/resources/integrations) available on Arcade -- Build your own [custom MCP Server](/build/create-tools/tool-basics/build-mcp-server) +- Explore the [MCP Servers](/en/resources/integrations) available on Arcade +- Build your own [custom MCP Server](/en/build/create-tools/tool-basics/build-mcp-server) diff --git a/content/docs/build/tool-calling/meta.json b/content/docs/build/tool-calling/meta.json new file mode 100644 index 000000000..f13176c83 --- /dev/null +++ b/content/docs/build/tool-calling/meta.json @@ -0,0 +1,8 @@ +{ + "title": "Call tools", + "pages": [ + "error-handling", + "call-third-party-apis", + "custom-apps" + ] +} diff --git a/content/docs/build/user-facing-agents/index.mdx b/content/docs/build/user-facing-agents/index.mdx new file mode 100644 index 000000000..21c78b0b4 --- /dev/null +++ b/content/docs/build/user-facing-agents/index.mdx @@ -0,0 +1,20 @@ +--- +title: User-facing agents +sidebarTitle: Overview +description: Ship agents that act on behalf of your end users, with production-ready auth +--- + +These guides are for **agent developers** shipping agents that act on behalf of end users, rather than only for themselves. Once real users sign in, the authorization flow they see becomes part of your product. + +<Cards num={2}> + <Cards.Card + title="Secure auth in production" + href="/en/build/user-facing-agents/secure-auth-production" + /> +</Cards> + +## Related + +- [Call tools with user authorization](/en/build/tool-calling/custom-apps/auth-tool-calling) to trigger and handle auth from your app +- [Auth providers](/en/references/auth-providers) to use your own OAuth app credentials and brand the consent screen +- [User Sources](/en/operate/identity/user-sources) if a platform operator connects your organization's identity provider diff --git a/content/docs/build/user-facing-agents/meta.json b/content/docs/build/user-facing-agents/meta.json new file mode 100644 index 000000000..a1da8fc08 --- /dev/null +++ b/content/docs/build/user-facing-agents/meta.json @@ -0,0 +1,7 @@ +{ + "title": "User-facing agents", + "pages": [ + "index", + "secure-auth-production" + ] +} diff --git a/app/en/build/user-facing-agents/secure-auth-production/page.mdx b/content/docs/build/user-facing-agents/secure-auth-production.mdx similarity index 96% rename from app/en/build/user-facing-agents/secure-auth-production/page.mdx rename to content/docs/build/user-facing-agents/secure-auth-production.mdx index b0c647361..809f44d12 100644 --- a/app/en/build/user-facing-agents/secure-auth-production/page.mdx +++ b/content/docs/build/user-facing-agents/secure-auth-production.mdx @@ -1,10 +1,10 @@ --- -title: "Secure Auth in Production" -description: "How to secure and brand your auth flows in production" +title: Secure and Brand the Auth Flow in Production +metaTitle: Secure Auth in Production +sidebarTitle: Secure auth in production +description: How to secure and brand your auth flows in production --- -# Secure and Brand the Auth Flow in Production - To keep your users safe, Arcade.dev performs a user verification check when a tool is authorized for the first time. This check verifies that the user who is authorizing the tool is the same user who started the authorization flow, which helps prevent phishing attacks. There are two ways to secure your auth flows with Arcade.dev: @@ -37,7 +37,7 @@ The Arcade.dev user verifier helps keep your auth flows secure while you are bui of your project. This ties end-user authorization to project membership, which is why the default OAuth apps are intended for development and single-tenant use. For a multi-user production app, add your own [OAuth app - credentials](/references/auth-providers) and pair them with a [custom user + credentials](/en/references/auth-providers) and pair them with a [custom user verifier](#build-a-custom-user-verifier), so your end users are verified by your app rather than by project membership. </Callout> @@ -50,8 +50,6 @@ When your users authorize a tool, Arcade.dev will redirect the user's browser to If you need help, join the [Implementing a custom user verifier](https://github.com/ArcadeAI/arcade-ai/discussions/486) GitHub discussion and we'll be happy to assist. -import { Steps, Tabs, Callout } from "nextra/components"; - <Steps> ### Build a new route diff --git a/app/en/get-started/about-arcade/page.mdx b/content/docs/get-started/about-arcade.mdx similarity index 79% rename from app/en/get-started/about-arcade/page.mdx rename to content/docs/get-started/about-arcade.mdx index d5fba3a66..e16a6f716 100644 --- a/app/en/get-started/about-arcade/page.mdx +++ b/content/docs/get-started/about-arcade.mdx @@ -1,25 +1,23 @@ --- -title: "About Arcade: the enterprise-ready actions runtime for AI agents" -description: "Arcade is the enterprise-ready actions runtime for AI agents — enforce security on every action (agent authorization), execute reliably across any system, and govern agents centrally." +title: About Arcade +metaTitle: "About Arcade: the enterprise-ready actions runtime for AI agents" +description: Arcade is the enterprise-ready actions runtime for AI agents — enforce security on every action (agent authorization), execute reliably across any system, and govern agents centrally. --- -import { Tabs } from "nextra/components"; -import { ArchitectureDiagram } from "@/app/_components/architecture-diagram"; - -# About Arcade +import { ArchitectureDiagram } from "@/components/architecture-diagram"; Arcade is the enterprise-ready actions runtime for AI agents. An agent isn't an agent if it can't take actions — and the moment an agent acts, three problems hit at once. Arcade solves all three at runtime, so teams ship production agents without rebuilding plumbing every time: - **Enforce — Agent Authorization:** Deploy agents your security team approves. Arcade handles OAuth and manages user tokens, API keys, and secrets, then enforces your existing IdP, DLP, SIEM, and compliance policies plus per-action authorization at runtime. -- **Execute — Agent-Optimized Tools:** Agents that work across every business system, with 7,500+ agent-optimized tools across 81 MCP servers, built for reliable execution at scale — not thin API wrappers. Not in the catalog? [Register a remote MCP server](/operate/governance/remote-mcp-servers) you or your vendors already run, and Arcade governs its tools the same way. +- **Execute — Agent-Optimized Tools:** Agents that work across every business system, with 7,500+ agent-optimized tools across 81 MCP servers, built for reliable execution at scale — not thin API wrappers. Not in the catalog? [Register a remote MCP server](/en/operate/governance/remote-mcp-servers) you or your vendors already run, and Arcade governs its tools the same way. - **Govern — Agent Lifecycle Governance:** Scale agents across the org from a central control plane: shared registry, version control, visibility filtering, and OpenTelemetry audit logs. Configure once and use any model, framework, and client. Deploy the first agent the same way you deploy the hundredth. ## Who these docs are for -- **Agent developers**: call tools from agents, connect MCP clients, or build custom MCP servers. Continue with [Get Started](/get-started/quickstarts/call-tool-agent) and [Build](/build). -- **Platform operators**: deploy Arcade, connect identity providers, and govern tool access for an organization. Start with [Operate](/operate). +- **Agent developers**: call tools from agents, connect MCP clients, or build custom MCP servers. Continue with [Get Started](/en/get-started/quickstarts/call-tool-agent) and [Build](/en/build). +- **Platform operators**: deploy Arcade, connect identity providers, and govern tool access for an organization. Start with [Operate](/en/operate). The rest of this page focuses on the first pillar — **agent authorization** — which is where most agentic applications hit their first wall. @@ -49,7 +47,7 @@ With Arcade, developers can now create agents that can _act as the end users of ## Auth permissions and scopes -Each tool in Arcade's MCP servers has a set of required permissions - or, more commonly referred to in OAuth 2.0, **scopes**. For example, the [`Gmail.SendEmail`](/resources/integrations/productivity/gmail#gmailsendemail) tool requires the [`https://www.googleapis.com/auth/gmail.send`](https://developers.google.com/identity/protocols/oauth2/scopes#gmail) scope. +Each tool in Arcade's MCP servers has a set of required permissions - or, more commonly referred to in OAuth 2.0, **scopes**. For example, the [`Gmail.SendEmail`](/en/resources/integrations/productivity/gmail#gmailsendemail) tool requires the [`https://www.googleapis.com/auth/gmail.send`](https://developers.google.com/identity/protocols/oauth2/scopes#gmail) scope. A scope is what the user has authorized someone else (in this case, the AI agent) to do on their behalf. In any OAuth 2.0-compatible service, each kind of action requires a different set of permissions. This gives the user fine-grained control over what data third-party services can access and what actions they can take in their accounts. @@ -57,11 +55,11 @@ When an agent calls a tool, if the user has not granted the required permissions ## How to implement OAuth 2.0-authorized tool calling -To learn how Arcade authorizes actions (tools) through OAuth 2.0 and how to implement auth flow, check out [Authorized Tool Calling](/build/tool-calling/custom-apps/auth-tool-calling). +To learn how Arcade authorizes actions (tools) through OAuth 2.0 and how to implement auth flow, check out [Authorized Tool Calling](/en/build/tool-calling/custom-apps/auth-tool-calling). ## Tools that don't require authorization -Some tools, like [`GoogleSearch.Search`](/resources/integrations/search/google_search#googlesearchsearch), allow AI agents to retrieve information or perform actions without needing user-specific authorization. +Some tools, like [`GoogleSearch.Search`](/en/resources/integrations/search/google_search#googlesearchsearch), allow AI agents to retrieve information or perform actions without needing user-specific authorization. <Tabs items={["Python", "JavaScript"]} storageKey="preferredLanguage"> <Tabs.Tab> diff --git a/content/docs/get-started/agent-frameworks/ag2/meta.json b/content/docs/get-started/agent-frameworks/ag2/meta.json new file mode 100644 index 000000000..d9abf3c20 --- /dev/null +++ b/content/docs/get-started/agent-frameworks/ag2/meta.json @@ -0,0 +1,4 @@ +{ + "title": "AG2", + "pages": ["use-arcade-tools"] +} diff --git a/app/en/get-started/agent-frameworks/ag2/use-arcade-tools/page.mdx b/content/docs/get-started/agent-frameworks/ag2/use-arcade-tools.mdx similarity index 96% rename from app/en/get-started/agent-frameworks/ag2/use-arcade-tools/page.mdx rename to content/docs/get-started/agent-frameworks/ag2/use-arcade-tools.mdx index dea5ec02c..6786a7b05 100644 --- a/app/en/get-started/agent-frameworks/ag2/use-arcade-tools/page.mdx +++ b/content/docs/get-started/agent-frameworks/ag2/use-arcade-tools.mdx @@ -1,12 +1,9 @@ --- -title: "Use Arcade tools with AG2" -description: "Learn how to use Arcade tools in AG2 multi-agent applications" +title: Use Arcade tools with AG2 +sidebarTitle: Setup Arcade tools with AG2 +description: Learn how to use Arcade tools in AG2 multi-agent applications --- -import { Steps, Tabs, Callout } from "nextra/components"; - -# Use Arcade tools with AG2 - [AG2](https://ag2.ai/) (formerly AutoGen) is an open-source framework for building multi-agent AI systems. It provides conversable agents that can work together, use tools, and interact with humans. This guide explains how to integrate Arcade tools into your AG2 applications. <GuideOverview> @@ -19,7 +16,7 @@ You will build an AG2 agent that uses Arcade tools to help users with Gmail and <GuideOverview.Prerequisites> - <DashboardLink path="register" /> -- [Obtain an Arcade API key](/get-started/setup/api-keys) +- [Obtain an Arcade API key](/en/get-started/setup/api-keys) - The [`uv` package manager](https://docs.astral.sh/uv/) </GuideOverview.Prerequisites> @@ -77,7 +74,7 @@ uv add "ag2[openai]" arcadepy Create a new file called `.env` and add the following environment variables: -```env filename=".env" +```env title=".env" # Arcade API key ARCADE_API_KEY=YOUR_ARCADE_API_KEY # Arcade user ID (this is the email address you used to login to Arcade) @@ -90,7 +87,7 @@ OPENAI_API_KEY=YOUR_OPENAI_API_KEY Create a new file called `main.py` and add the following code: -```python filename="main.py" +```python title="main.py" import inspect import os from typing import Any @@ -114,7 +111,7 @@ from dotenv import load_dotenv ### Configure the agent -```python filename="main.py" +```python title="main.py" load_dotenv() # The Arcade user ID identifies who is authorizing each service @@ -135,7 +132,7 @@ AGENT_NAME = "Assistant" This utility maps Arcade's type system to Python types, which AG2 uses to generate the function-calling schema for the LLM. -```python filename="main.py" +```python title="main.py" TYPE_MAP: dict[str, type] = { "string": str, "number": float, @@ -166,7 +163,7 @@ This function handles tool authorization and execution through the Arcade client reduces context bloat. </Callout> -```python filename="main.py" +```python title="main.py" def call_arcade_tool( client: Arcade, tool_name: str, inputs: dict, user_id: str ) -> dict: @@ -193,7 +190,7 @@ def call_arcade_tool( AG2 registers tools as typed Python functions. Since tools are retrieved dynamically from Arcade, you need to build these functions at runtime. This helper creates a function for each tool definition, using `inspect.Parameter` and `inspect.Signature` to set the correct parameter names, types, and defaults so that AG2 can generate the correct function-calling schema. -```python filename="main.py" +```python title="main.py" def _build_tool_function( tool_def: ToolDefinition, client: Arcade, @@ -243,7 +240,7 @@ def _build_tool_function( This function retrieves tool definitions from the Arcade API and returns a list of dynamically-built Python functions ready to register with AG2. -```python filename="main.py" +```python title="main.py" def get_arcade_tools( client: Arcade, *, @@ -276,7 +273,7 @@ def get_arcade_tools( Create the `AssistantAgent` and `ConversableAgent`, retrieve the Arcade tools, and register each dynamically-generated function. This tells the assistant which tools it can call and the user proxy how to execute them. -```python filename="main.py" +```python title="main.py" # Initialize the Arcade client client = Arcade(api_key=os.environ["ARCADE_API_KEY"]) @@ -315,7 +312,7 @@ for func in arcade_tools: ### Start the conversation -```python filename="main.py" +```python title="main.py" if __name__ == "__main__": print("Assistant ready. Type your request (or 'exit' to quit).") while True: @@ -362,7 +359,7 @@ Now that you have integrated Arcade tools into your AG2 agent, you can: <details> <summary>**main.py** (full file)</summary> -```python filename="main.py" +```python title="main.py" import inspect import os from typing import Any diff --git a/app/en/get-started/agent-frameworks/copilotkit/page.mdx b/content/docs/get-started/agent-frameworks/copilotkit.mdx similarity index 96% rename from app/en/get-started/agent-frameworks/copilotkit/page.mdx rename to content/docs/get-started/agent-frameworks/copilotkit.mdx index 4158c7039..1076ed5e7 100644 --- a/app/en/get-started/agent-frameworks/copilotkit/page.mdx +++ b/content/docs/get-started/agent-frameworks/copilotkit.mdx @@ -1,12 +1,9 @@ --- -title: "Build an AI agent with Arcade and CopilotKit" -description: "Give a CopilotKit agent OAuth-backed Arcade tools and render the authorization step as generative UI" +title: Build an AI agent with Arcade and CopilotKit +sidebarTitle: CopilotKit +description: Give a CopilotKit agent OAuth-backed Arcade tools and render the authorization step as generative UI --- -import { Steps, Tabs, Callout } from "nextra/components"; - -# Build an AI agent with Arcade and CopilotKit - Arcade is an MCP runtime for production agents: it brokers per-user OAuth, vaults and refreshes tokens, and runs a catalog of agent-optimized tools, all without the credentials ever reaching the LLM. [CopilotKit](https://docs.copilotkit.ai) is the React framework that provides the agent runtime and the chat UI. In this guide, you'll give a CopilotKit Built-in Agent a set of Arcade-backed tools (send Gmail, read the inbox, search Google News) and render Arcade's one-time authorization step as a generative-UI Connect card right in the chat. The user approves access once, and the agent completes the action. @@ -105,7 +102,7 @@ This guide uses CopilotKit's v2 Built-in Agent APIs (the `@copilotkit/runtime/v2 Add your keys to **.env.local**: -```env filename=".env.local" +```env title=".env.local" ARCADE_API_KEY={arcade_api_key} ARCADE_USER_ID={arcade_user_id} OPENAI_API_KEY=your_openai_api_key @@ -121,7 +118,7 @@ This helper is the heart of the integration. `runArcadeTool` authorizes the user **Failures come back as data, not exceptions.** Arcade reports a tool's *runtime* failures as data (`success === false` / `output.error`), not as a thrown exception. If you only read `output.value`, a failed send renders a green "success" card, so the helper checks for errors explicitly and returns an `{ error }` shape the UI can show. </Callout> -```ts filename="lib/arcade.ts" +```ts title="lib/arcade.ts" import Arcade from "@arcadeai/arcadejs"; // Created lazily so the module can be imported during `next build` without the @@ -199,7 +196,7 @@ export async function runArcadeTool({ Each CopilotKit tool is a thin `defineTool` wrapper that calls `runArcadeTool` with an Arcade tool name. `searchNews` needs no auth; Gmail does. Keep tool descriptions about *what the tool does*. The agent learns the Connect-then-retry protocol from the system prompt and the tool's result, not from prose in the description it can misread. Match each tool's parameter names to the Arcade tool's own schema, or unknown params are silently dropped. -```ts filename="app/api/copilotkit/route.ts" +```ts title="app/api/copilotkit/route.ts" import { BuiltInAgent, CopilotRuntime, @@ -322,7 +319,7 @@ On the client, `useRenderTool` subscribes to each tool call. When the result is The snippet below defines small inline `LoadingCard`, `ErrorCard`, `EmailSentCard`, and `AuthorizationCard` components so it stands alone. -```tsx filename="app/page.tsx" +```tsx title="app/page.tsx" "use client"; import { CopilotChat, useRenderTool } from "@copilotkit/react-core/v2"; import { z } from "zod"; @@ -426,7 +423,7 @@ export default function Page() { The `<CopilotKit>` provider is a client component, so wrap it in a small `"use client"` file, then use that wrapper from your server `app/layout.tsx` (which also imports the v2 stylesheet): -```tsx filename="app/providers.tsx" +```tsx title="app/providers.tsx" "use client"; import { CopilotKit } from "@copilotkit/react-core/v2"; @@ -439,7 +436,7 @@ export function Providers({ children }: { children: React.ReactNode }) { } ``` -```tsx filename="app/layout.tsx" +```tsx title="app/layout.tsx" import "@copilotkit/react-core/v2/styles.css"; import { Providers } from "./providers"; @@ -489,11 +486,11 @@ Find the latest news on open-source AI agents and email me a 3-bullet summary. ## Next steps -- **Add more tools**: Browse the [tool catalog](/resources/integrations) and add tools for GitHub, Notion, Linear, and more. +- **Add more tools**: Browse the [tool catalog](/en/resources/integrations) and add tools for GitHub, Notion, Linear, and more. - **Scale from three tools to thousands**: Instead of hand-writing a `defineTool` per tool, pull formatted tool definitions from Arcade and generate the wrappers. <Callout type="info"> - **Building a multi-user app?** This guide uses a single `ARCADE_USER_ID` for local testing. For production apps where each user needs their own OAuth tokens, see [Secure auth for production](/build/user-facing-agents/secure-auth-production) to learn how to resolve the Arcade `user_id` per request from a server-verified session and authenticate the runtime. + **Building a multi-user app?** This guide uses a single `ARCADE_USER_ID` for local testing. For production apps where each user needs their own OAuth tokens, see [Secure auth for production](/en/build/user-facing-agents/secure-auth-production) to learn how to resolve the Arcade `user_id` per request from a server-verified session and authenticate the runtime. </Callout> ## Complete code @@ -501,7 +498,7 @@ Find the latest news on open-source AI agents and email me a 3-bullet summary. <details> <summary>**lib/arcade.ts** (full file)</summary> -```ts filename="lib/arcade.ts" +```ts title="lib/arcade.ts" import Arcade from "@arcadeai/arcadejs"; let arcadeClient: Arcade | undefined; @@ -570,7 +567,7 @@ export async function runArcadeTool({ <details> <summary>**app/api/copilotkit/route.ts** (full file)</summary> -```ts filename="app/api/copilotkit/route.ts" +```ts title="app/api/copilotkit/route.ts" import { BuiltInAgent, CopilotRuntime, @@ -669,7 +666,7 @@ export const OPTIONS = handler; <details> <summary>**app/page.tsx** (full file)</summary> -```tsx filename="app/page.tsx" +```tsx title="app/page.tsx" "use client"; import { CopilotChat, useRenderTool } from "@copilotkit/react-core/v2"; import { z } from "zod"; @@ -772,7 +769,7 @@ export default function Page() { <details> <summary>**app/providers.tsx** (full file)</summary> -```tsx filename="app/providers.tsx" +```tsx title="app/providers.tsx" "use client"; import { CopilotKit } from "@copilotkit/react-core/v2"; @@ -790,7 +787,7 @@ export function Providers({ children }: { children: React.ReactNode }) { <details> <summary>**app/layout.tsx** (full file)</summary> -```tsx filename="app/layout.tsx" +```tsx title="app/layout.tsx" import "@copilotkit/react-core/v2/styles.css"; import { Providers } from "./providers"; diff --git a/content/docs/get-started/agent-frameworks/crewai/meta.json b/content/docs/get-started/agent-frameworks/crewai/meta.json new file mode 100644 index 000000000..164a5bb3f --- /dev/null +++ b/content/docs/get-started/agent-frameworks/crewai/meta.json @@ -0,0 +1,4 @@ +{ + "title": "CrewAI", + "pages": ["use-arcade-tools"] +} diff --git a/app/en/get-started/agent-frameworks/crewai/use-arcade-tools/page.mdx b/content/docs/get-started/agent-frameworks/crewai/use-arcade-tools.mdx similarity index 97% rename from app/en/get-started/agent-frameworks/crewai/use-arcade-tools/page.mdx rename to content/docs/get-started/agent-frameworks/crewai/use-arcade-tools.mdx index 8455a60fd..d79ec0d05 100644 --- a/app/en/get-started/agent-frameworks/crewai/use-arcade-tools/page.mdx +++ b/content/docs/get-started/agent-frameworks/crewai/use-arcade-tools.mdx @@ -1,12 +1,10 @@ --- -title: "Setup Arcade tools with CrewAI" -description: "Learn how to use Arcade tools in CrewAI applications" +title: Use Arcade tools with CrewAI +metaTitle: Setup Arcade tools with CrewAI +sidebarTitle: Setup Arcade tools with CrewAI +description: Learn how to use Arcade tools in CrewAI applications --- -import { Steps, Tabs, Callout } from "nextra/components"; - -# Use Arcade tools with CrewAI - [CrewAI](https://www.crewai.com/) is an agentic framework optimized for building task-oriented multi-agent systems. This guide explains how to integrate Arcade tools into your CrewAI applications. <GuideOverview> @@ -19,7 +17,7 @@ You will build a CrewAI agent that uses Arcade tools to help users with Gmail an <GuideOverview.Prerequisites> - <DashboardLink path="register" /> -- [Obtain an Arcade API key](/get-started/setup/api-keys) +- [Obtain an Arcade API key](/en/get-started/setup/api-keys) - The [`uv` package manager](https://docs.astral.sh/uv/) </GuideOverview.Prerequisites> @@ -77,7 +75,7 @@ uv add 'crewai[tools]' arcadepy Create a new file called `.env` and add the following environment variables: -```env filename=".env" +```env title=".env" # Arcade API key ARCADE_API_KEY=YOUR_ARCADE_API_KEY # Arcade user ID (this is the email address you used to login to Arcade) @@ -90,7 +88,7 @@ OPENAI_API_KEY=YOUR_OPENAI_API_KEY Create a new file called `main.py` and add the following code: -```python filename="main.py" +```python title="main.py" from typing import Any from arcadepy import Arcade from arcadepy.types import ToolDefinition @@ -122,7 +120,7 @@ This includes many imports, here's a breakdown: The rest of the code uses these variables to customize the agent and manage the tools. Feel free to configure them to your liking. Here, the `EventListener` class is used to suppress CrewAI's rich panel output, which is useful for debugging but verbose for an interactive session like the one you're building. -```python filename="main.py" +```python title="main.py" # Load environment variables from the .env file load_dotenv() @@ -151,7 +149,7 @@ EventListener().formatter.verbose = False In this utility function, you transform an Arcade tool definition into a Pydantic model. Later, you will transform these models to construct tools in the format expected by CrewAI. The `_build_args_model` function extracts the tools' parameters, name, and description, and maps them to a Pydantic model. -```python filename="main.py" +```python title="main.py" TYPE_MAP: dict[str, type] = { "string": str, "number": float, @@ -200,7 +198,7 @@ Here, you define the `ArcadeTool` class that extends the CrewAI `BaseTool` class reduces context bloat. </Callout> -```python filename="main.py" +```python title="main.py" class ArcadeTool(BaseTool): """A CrewAI tool backed by an Arcade tool definition.""" @@ -253,7 +251,7 @@ Here's a breakdown of what it does for clarity: - retrieve individual tools (defined in the `TOOLS` variable) - transform the Arcade tools to CrewAI tools with the `ArcadeTool` class you defined earlier -```python filename="main.py" +```python title="main.py" def get_arcade_tools( client: Arcade, *, @@ -300,7 +298,7 @@ The main function is where you: - Initialize the conversation - Run the loop -```python filename="main.py" +```python title="main.py" def main(): client = Arcade() @@ -367,7 +365,7 @@ Now that you have integrated Arcade tools into your CrewAI agent team, you can: <details> <summary>**main.py** (full file)</summary> -```python filename="main.py" +```python title="main.py" from typing import Any from arcadepy import Arcade from arcadepy.types import ToolDefinition diff --git a/content/docs/get-started/agent-frameworks/google-adk/meta.json b/content/docs/get-started/agent-frameworks/google-adk/meta.json new file mode 100644 index 000000000..c24fc02dc --- /dev/null +++ b/content/docs/get-started/agent-frameworks/google-adk/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Google ADK", + "pages": ["overview", "setup-python", "setup-typescript"] +} diff --git a/app/en/get-started/agent-frameworks/google-adk/overview/page.mdx b/content/docs/get-started/agent-frameworks/google-adk/overview.mdx similarity index 68% rename from app/en/get-started/agent-frameworks/google-adk/overview/page.mdx rename to content/docs/get-started/agent-frameworks/google-adk/overview.mdx index 035de4f25..8962b23a8 100644 --- a/app/en/get-started/agent-frameworks/google-adk/overview/page.mdx +++ b/content/docs/get-started/agent-frameworks/google-adk/overview.mdx @@ -1,10 +1,9 @@ --- -title: "Arcade with Google ADK" -description: "Integrate Arcade tools with Google ADK agents" +title: Arcade with Google ADK +sidebarTitle: Overview +description: Integrate Arcade tools with Google ADK agents --- -# Arcade with Google ADK - [Google ADK](https://github.com/google/adk-python/) is a modular framework for building and deploying AI agents. It's optimized for Gemini and the Google ecosystem. Arcade integrates with both the Python and TypeScript versions, giving your agents access to Gmail, GitHub, Slack, and 7,500+ other tools. ## Get started @@ -12,7 +11,7 @@ description: "Integrate Arcade tools with Google ADK agents" Choose your language to set up Arcade with Google ADK: - **[Python setup](/en/get-started/agent-frameworks/google-adk/setup-python)** - Build an agent with Arcade tools using the `google-adk-arcade` package -- **[TypeScript setup](/get-started/agent-frameworks/google-adk/setup-typescript)** - Build an agent with Arcade tools using `@arcadeai/arcadejs` +- **[TypeScript setup](/en/get-started/agent-frameworks/google-adk/setup-typescript)** - Build an agent with Arcade tools using `@arcadeai/arcadejs` ## What you can build @@ -24,4 +23,4 @@ With Arcade and Google ADK, your agents can: - Search the web and extract content - Access 7,500+ other integrations -Browse the [full MCP server catalog](/resources/integrations) to see all available tools. +Browse the [full MCP server catalog](/en/resources/integrations) to see all available tools. diff --git a/app/en/get-started/agent-frameworks/google-adk/setup-python/page.mdx b/content/docs/get-started/agent-frameworks/google-adk/setup-python.mdx similarity index 97% rename from app/en/get-started/agent-frameworks/google-adk/setup-python/page.mdx rename to content/docs/get-started/agent-frameworks/google-adk/setup-python.mdx index 22dd27025..7715c0801 100644 --- a/app/en/get-started/agent-frameworks/google-adk/setup-python/page.mdx +++ b/content/docs/get-started/agent-frameworks/google-adk/setup-python.mdx @@ -1,12 +1,9 @@ --- -title: "Setup Arcade with Google ADK (Python)" -description: "Build an agent with Arcade tools using Google ADK" +title: Setup Arcade with Google ADK (Python) +sidebarTitle: Setup (Python) +description: Build an agent with Arcade tools using Google ADK --- -import { Steps, Tabs, Callout } from "nextra/components"; - -# Setup Arcade with Google ADK (Python) - Google ADK is a modular framework for building and deploying AI agents. It optimizes for Gemini and the Google Ecosystem, but supports any model. <GuideOverview> @@ -77,11 +74,11 @@ uv add arcadepy google-adk Provide your Arcade and Google API keys. You can store it in environment variables or directly in your code: -> Need an Arcade API key? Visit the [Get an API key](/get-started/setup/api-keys) page to create one. +> Need an Arcade API key? Visit the [Get an API key](/en/get-started/setup/api-keys) page to create one. Create a new file called `.env` and add the following environment variables: -```env filename=".env" +```env title=".env" # Arcade API key ARCADE_API_KEY=YOUR_ARCADE_API_KEY # Arcade user ID (this is the email address you used to login to Arcade) @@ -96,7 +93,7 @@ GOOGLE_GENAI_USE_VERTEXAI=FALSE Create a new file called `main.py` and add the following code: -```python filename="main.py" +```python title="main.py" from arcadepy import AsyncArcade from arcadepy.types import ToolDefinition from arcadepy.types.execute_tool_response import ExecuteToolResponse @@ -147,7 +144,7 @@ This includes multiple imports, here's a breakdown: These variables set the configuration for the rest of the code to customize the agent and manage the tools. Feel free to configure them to your liking. Set the `google_genai.types` logging level to `ERROR` to avoid a lot of noise in the console. Load the environment variables from the `.env` file using `load_dotenv()`. -```python filename="main.py" +```python title="main.py" logging.getLogger("google_genai.types").setLevel(logging.ERROR) load_dotenv() @@ -170,7 +167,7 @@ AGENT_NAME = "AwesomeAgent" In this utility function, you transform an Arcade tool definition into a Pydantic model. Later, you will transform these models to construct tools in the format expected by Google ADK. The `tool_definition_to_pydantic_model` function extracts the tools' parameters, name, and description, and maps them to a Pydantic model. -```python filename="main.py" +```python title="main.py" # Mapping of Arcade value types to Python types TYPE_MAPPING = { "string": str, @@ -229,7 +226,7 @@ You also define a `ToolError` class to handle errors from the Arcade tools. It w reduces context bloat. </Callout> -```python filename="main.py" +```python title="main.py" class ToolError(ValueError): def __init__(self, result: ExecuteToolResponse): self.result = result @@ -338,7 +335,7 @@ Here's a breakdown of what it does for clarity: - retrieve individual tools (defined in the `TOOLS` variable) - transform the Arcade tools to Google ADK tools with the `ArcadeTool` class you defined earlier -```python filename="main.py" +```python title="main.py" async def get_arcade_tools( client: AsyncArcade | None = None, tools: list[str] | None = None, @@ -396,7 +393,7 @@ The main function is where you: Google ADK provides a `Runner` class that manages the agentic loop, and will employ the session and artifact services you created earlier to store the conversation history and agent state. Therefore, you don't need to manually store the conversation history or agent state, and you can just pass the latest user message to the runner. -```python filename="main.py" +```python title="main.py" async def main(): session_service = InMemorySessionService() @@ -485,7 +482,7 @@ Now that you have integrated Arcade tools into your Google ADK application, you <details> <summary>**main.py** (full file)</summary> -```python filename="main.py" +```python title="main.py" from arcadepy import AsyncArcade from arcadepy.types import ToolDefinition from arcadepy.types.execute_tool_response import ExecuteToolResponse diff --git a/app/en/get-started/agent-frameworks/google-adk/setup-typescript/page.mdx b/content/docs/get-started/agent-frameworks/google-adk/setup-typescript.mdx similarity index 95% rename from app/en/get-started/agent-frameworks/google-adk/setup-typescript/page.mdx rename to content/docs/get-started/agent-frameworks/google-adk/setup-typescript.mdx index 8cc1b03e0..a0ff4bfe4 100644 --- a/app/en/get-started/agent-frameworks/google-adk/setup-typescript/page.mdx +++ b/content/docs/get-started/agent-frameworks/google-adk/setup-typescript.mdx @@ -1,12 +1,9 @@ --- -title: "Setup Arcade with Google ADK (TypeScript)" -description: "Build an agent with Arcade tools using Google ADK for TypeScript" +title: Setup Arcade with Google ADK (TypeScript) +sidebarTitle: Setup (TypeScript) +description: Build an agent with Arcade tools using Google ADK for TypeScript --- -import { Steps, Tabs, Callout } from "nextra/components"; - -# Setup Arcade with Google ADK (TypeScript) - [Google ADK for TypeScript](https://github.com/google/adk-js) provides a framework for building AI agents in TypeScript. Arcade's `@arcadeai/arcadejs` library provides the tools integration, allowing your agents to access Gmail, GitHub, Slack, and 7,500+ other services. <GuideOverview> @@ -19,7 +16,7 @@ Build an agent that uses Arcade tools to help users with Gmail and Slack <GuideOverview.Prerequisites> - <DashboardLink path="register" /> -- [Obtain an Arcade API key](/get-started/setup/api-keys) +- [Obtain an Arcade API key](/en/get-started/setup/api-keys) - [Node.js](https://nodejs.org/) 18+ (includes npm) or [Bun](https://bun.sh/) - A [Gemini API key](https://aistudio.google.com/apikey) @@ -74,7 +71,7 @@ bun add @google/adk @arcadeai/arcadejs dotenv Create a `.env` file with your API keys: -```env filename=".env" +```env title=".env" # Arcade API key from https://app.arcade.dev/api-keys ARCADE_API_KEY=YOUR_ARCADE_API_KEY # Your Arcade user ID (the email you used to sign up) @@ -87,7 +84,7 @@ GEMINI_API_KEY=YOUR_GEMINI_API_KEY Create `index.ts` with the following imports and configuration: -```typescript filename="index.ts" +```typescript title="index.ts" import { LlmAgent, FunctionTool, @@ -117,7 +114,7 @@ const APP_NAME = "inbox_assistant"; Use Arcade's `toZod` to convert tool definitions to Zod schemas, then wrap them in Google ADK's `FunctionTool`: -```typescript filename="index.ts" +```typescript title="index.ts" async function getArcadeTools(client: Arcade, userId: string): Promise<FunctionTool[]> { // Fetch tools from MCP servers const mcpServerTools = await Promise.all( @@ -197,7 +194,7 @@ async function getArcadeTools(client: Arcade, userId: string): Promise<FunctionT ### Create and run the agent -```typescript filename="index.ts" +```typescript title="index.ts" async function main() { const client = new Arcade(); const sessionService = new InMemorySessionService(); @@ -311,7 +308,7 @@ Google ADK for TypeScript is still changing. Check the [official documentation]( <details> <summary>**index.ts** (full file)</summary> -```typescript filename="index.ts" +```typescript title="index.ts" import { LlmAgent, FunctionTool, @@ -494,4 +491,4 @@ main().catch(console.error); - Add more tools by modifying `MCP_SERVERS` and `INDIVIDUAL_TOOLS` - Build multi-agent systems with different Arcade tools -- Explore [creating custom tools](/build/create-tools/tool-basics/build-mcp-server) with the Arcade Tool SDK +- Explore [creating custom tools](/en/build/create-tools/tool-basics/build-mcp-server) with the Arcade Tool SDK diff --git a/app/en/get-started/agent-frameworks/page.mdx b/content/docs/get-started/agent-frameworks/index.mdx similarity index 77% rename from app/en/get-started/agent-frameworks/page.mdx rename to content/docs/get-started/agent-frameworks/index.mdx index 08ce38a4b..927e7753e 100644 --- a/app/en/get-started/agent-frameworks/page.mdx +++ b/content/docs/get-started/agent-frameworks/index.mdx @@ -1,6 +1,9 @@ -import { AgentFrameworkTabs } from "@/app/_components/agent-framework-tabs"; +--- +title: Arcade with Agent Frameworks and MCP Clients +sidebarTitle: Overview +--- -# Arcade with Agent Frameworks and MCP Clients +import { AgentFrameworkTabs } from "@/components/agent-framework-tabs"; Arcade integrates with agent frameworks and MCP clients to add tool-calling capabilities to your AI applications. diff --git a/app/en/get-started/agent-frameworks/langchain/auth-langchain-tools/page.mdx b/content/docs/get-started/agent-frameworks/langchain/auth-langchain-tools.mdx similarity index 96% rename from app/en/get-started/agent-frameworks/langchain/auth-langchain-tools/page.mdx rename to content/docs/get-started/agent-frameworks/langchain/auth-langchain-tools.mdx index a552fb3e1..fc505f6a3 100644 --- a/app/en/get-started/agent-frameworks/langchain/auth-langchain-tools/page.mdx +++ b/content/docs/get-started/agent-frameworks/langchain/auth-langchain-tools.mdx @@ -1,10 +1,9 @@ --- -title: "Authorize Existing Tools" -description: "Use Arcade to authorize existing tools" +title: Authorize Existing Tools +sidebarTitle: Authorizing Existing Tools +description: Use Arcade to authorize existing tools --- -import { Steps, Tabs, Callout } from "nextra/components"; - ## Authorize Existing Tools In this guide, we'll show you how to authorize LangChain tools like the `GmailToolkit` using Arcade. You may already have tools you want to use, and this guide will show you how to authorize them. Arcade handles retrieving, authorizing, and managing tokens so you don't have to. @@ -13,7 +12,7 @@ In this guide, we'll show you how to authorize LangChain tools like the `GmailTo ### Prerequisites -- [Obtain an Arcade API key](/get-started/setup/api-keys) +- [Obtain an Arcade API key](/en/get-started/setup/api-keys) ### Install the required packages diff --git a/content/docs/get-started/agent-frameworks/langchain/meta.json b/content/docs/get-started/agent-frameworks/langchain/meta.json new file mode 100644 index 000000000..534ff6007 --- /dev/null +++ b/content/docs/get-started/agent-frameworks/langchain/meta.json @@ -0,0 +1,9 @@ +{ + "title": "LangChain", + "pages": [ + "overview", + "use-arcade-with-langchain-py", + "use-arcade-with-langchain-ts", + "auth-langchain-tools" + ] +} diff --git a/content/docs/get-started/agent-frameworks/langchain/overview.mdx b/content/docs/get-started/agent-frameworks/langchain/overview.mdx new file mode 100644 index 000000000..14e5d72e8 --- /dev/null +++ b/content/docs/get-started/agent-frameworks/langchain/overview.mdx @@ -0,0 +1,30 @@ +--- +title: Arcade with LangChain +sidebarTitle: Overview +description: Integrate Arcade tools with LangChain agents +--- + +[LangChain](https://www.langchain.com/) is a popular framework for building AI agents that abstracts much of the complexity of agent development. Arcade integrates with both the Python and JavaScript versions, giving your agents access to Gmail, GitHub, Slack, and 7,500+ other tools. + +## Get started + +Choose your language to set up Arcade with LangChain: + +- **[Python setup](/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py)** - Build an agent with Arcade tools using `langchain-arcade` +- **[TypeScript setup](/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts)** - Build an agent with Arcade tools using `@arcadeai/arcadejs` + +## What you can build + +With Arcade and LangChain, your agents can: + +- Read and send emails via Gmail +- Post messages to Slack channels +- Create GitHub issues and pull requests +- Search the web and extract content +- Access 7,500+ other integrations + +Browse the [full MCP server catalog](/en/resources/integrations) to see all available tools. + +## Advanced topics + +- **[Authorizing existing tools](/en/get-started/agent-frameworks/langchain/auth-langchain-tools)** - Add Arcade authorization to your existing LangChain tools diff --git a/app/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py/page.mdx b/content/docs/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py.mdx similarity index 98% rename from app/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py/page.mdx rename to content/docs/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py.mdx index 2669dedc0..959f6082a 100644 --- a/app/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py/page.mdx +++ b/content/docs/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py.mdx @@ -1,12 +1,9 @@ --- -title: "Setup Arcade with LangChain" -description: "Learn how to use Arcade tools in LangChain agents" +title: Setup Arcade with LangChain +sidebarTitle: Setup (Python) +description: Learn how to use Arcade tools in LangChain agents --- -import { Steps, Tabs, Callout } from "nextra/components"; - -# Setup Arcade with LangChain - LangChain is a popular agentic framework that abstracts a lot of the complexity of building AI agents. It is built on top of LangGraph, a lower level orchestration framework that offers more control over the inner flow of the agent. <GuideOverview> @@ -77,7 +74,7 @@ uv add arcadepy langchain langchain-openai python-dotenv Create a new file called `.env` and add the following API keys: -```env filename=".env" +```env title=".env" ARCADE_API_KEY=YOUR_ARCADE_API_KEY OPENAI_API_KEY=YOUR_OPENAI_API_KEY ``` @@ -86,7 +83,7 @@ OPENAI_API_KEY=YOUR_OPENAI_API_KEY Create a new file called `main.py` and add the following code: -```python filename="main.py" +```python title="main.py" import asyncio import os from typing import Dict, Any, List @@ -129,7 +126,7 @@ This is quite a number of imports, let's break them down: The rest of the code uses these variables to customize the agent and manage the tools. Feel free to configure them to your liking. -```python filename="main.py" +```python title="main.py" # Load environment variables from the .env file load_dotenv() @@ -154,7 +151,7 @@ Here you convert the Arcade tools to LangChain tools. You use the `arcade_schema The `arcade_to_langchain` function wraps the Arcade client and dynamically creates a `tool_function` that executes the tool and handles the authorization flow using the `interrupt` function. Once the tool is authorized, the `tool_function` will use the Arcade client to execute the tool with the provided arguments, and handle any errors that may occur. -```python filename="main.py" +```python title="main.py" TYPE_MAPPING = { "string": str, "number": float, @@ -268,7 +265,7 @@ async def arcade_to_langchain( In this helper function you use the Arcade client to retrieved the tools you configured at the beginning of the `main.py` file. You will use a dictionary to store the tools and avoid possible duplicates that may occur if you retrieve the same tool in the `TOOLS` and `MCP_SERVERS` variables. After retrieving all the tools, you will call the `arcade_to_langchain` function to convert the Arcade tools to LangChain tools. -```python filename="main.py" +```python title="main.py" async def get_arcade_tools( arcade_client: AsyncArcade | None = None, mcp_servers: List[str] | None = None, @@ -322,7 +319,7 @@ This helper function receives an interrupt and returns a decision object. Decisi authorization-related traces, which reduces the risk of hallucinations. </Callout> -```python filename="main.py" +```python title="main.py" async def handle_authorization_interrupt( interrupt_value: Dict[str, Any], arcade_client: AsyncArcade @@ -364,7 +361,7 @@ async def handle_authorization_interrupt( This last helper function handles the streaming of the agent's response, and captures the interrupts. When an interrupt is detected, it is added to the `interrupts` array, and the flow is interrupted. If there are no interrupts, it will just stream the agent's to your console. -```python filename="main.py" +```python title="main.py" async def stream_agent_response(agent, input_data, config) -> List[Any]: interrupts = [] @@ -402,7 +399,7 @@ Finally, write the main function that will create the agent, initialize the conv Here the `config` object is used to configure the `thread_id`, which tells the agent to store the state of the conversation into that specific thread. In the main function you will also initialize the checkpointer, and handle route the interrupts to the handles you wrote earlier. Notice how a single turn of the agentic loop may have multiple interrupts, and you need to handle them all before continuing to the next turn. -```python filename="main.py" +```python title="main.py" async def main(): # Initialize Arcade client arcade = AsyncArcade() @@ -531,7 +528,7 @@ You should see the agent responding to your prompts like any model, as well as h <details> <summary>**main.py** (full file)</summary> -```python filename="main.py" +```python title="main.py" import asyncio import os from typing import Dict, Any, List diff --git a/app/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts/page.mdx b/content/docs/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts.mdx similarity index 98% rename from app/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts/page.mdx rename to content/docs/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts.mdx index 7f7c0944f..4efd4fcfd 100644 --- a/app/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts/page.mdx +++ b/content/docs/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts.mdx @@ -1,12 +1,9 @@ --- -title: "Setup Arcade with LangChain" -description: "Learn how to use Arcade tools in LangChain agents" +title: Setup Arcade with LangChain +sidebarTitle: Setup (TypeScript) +description: Learn how to use Arcade tools in LangChain agents --- -import { Steps, Tabs, Callout } from "nextra/components"; - -# Setup Arcade with LangChain - Learn how to integrate Arcade tools using LangChain primitives to build AI agents. LangChain is a popular agentic framework that abstracts a lot of the complexity of building AI agents. LangGraph, a lower level orchestration framework, builds it and offers more control over the inner flow of the agent. @@ -57,7 +54,7 @@ bun install @arcadeai/arcadejs langchain @langchain/openai @langchain/core @lang Create a new file called `.env` and add the following API keys: -```env filename=".env" +```env title=".env" ARCADE_API_KEY=YOUR_ARCADE_API_KEY OPENAI_API_KEY=YOUR_OPENAI_API_KEY ``` @@ -66,7 +63,7 @@ OPENAI_API_KEY=YOUR_OPENAI_API_KEY Create a new file called `main.ts` and add the following code: -```ts filename="main.ts" +```ts title="main.ts" "use strict"; import { Arcade } from "@arcadeai/arcadejs"; import { @@ -109,7 +106,7 @@ This is a number of imports, examine them: These variables are used in the rest of the code to customize the agent and manage the tools. Feel free to configure them to your liking. -```ts filename="main.ts" +```ts title="main.ts" // configure your own values to customize your agent // The Arcade User ID identifies who is authorizing each service. @@ -133,7 +130,7 @@ const threadID = "1"; This is a wrapper around the `executeZodTool` function. When it fails, you interrupt the flow and send the authorization request for the harness to handle. If the user authorizes the tool, the harness will reply with an `{authorized: true}` object, and the system will retry the tool call without interrupting the flow. -```ts filename="main.ts" +```ts title="main.ts" function executeOrInterruptTool({ zodToolSchema, toolDefinition, @@ -204,7 +201,7 @@ This helper function is long, here's a breakdown of what it does for clarity: You then call the `getTools` function to get the tools you want the agent to use. -```ts filename="main.ts" +```ts title="main.ts" // Initialize the Arcade client const arcade = new Arcade(); @@ -296,7 +293,7 @@ This helper function receives an interrupt and returns a decision object. Decisi reduces context bloat. </Callout> -```ts filename="main.ts" +```ts title="main.ts" async function handleAuthInterrupt( interrupt: Interrupt, ): Promise<{ authorized: boolean }> { @@ -328,7 +325,7 @@ async function handleAuthInterrupt( Here you create the agent using the `createAgent` function. You pass the system prompt, the model, the tools, and the checkpointer. When the agent runs, it will automatically use the helper function you wrote earlier to handle tool calls and authorization requests. -```ts filename="main.ts" +```ts title="main.ts" const agent = createAgent({ systemPrompt: systemPrompt, model: agentModel, @@ -341,7 +338,7 @@ const agent = createAgent({ This last helper function handles the streaming of the agent's response, and captures the interrupts. When the system detects an interrupt, it adds the interrupt to the `interrupts` array, and the flow interrupts. If there are no interrupts, it will just stream the agent's to your console. -```ts filename="main.ts" +```ts title="main.ts" async function streamAgent( agent: any, input: any, @@ -382,7 +379,7 @@ Here the `config` object configures the `thread_id`, which tells the agent to st 1. Handle any errors 1. Exit the loop if the user wants to quit -```ts filename="main.ts" +```ts title="main.ts" async function main() { const config = { configurable: { thread_id: threadID } }; const rl = readline.createInterface({ @@ -466,7 +463,7 @@ You should see the agent responding to your prompts like any model, as well as h <details> <summary>**main.ts** (full file)</summary> -```ts filename="main.ts" +```ts title="main.ts" "use strict"; import { Arcade } from "@arcadeai/arcadejs"; import { diff --git a/app/en/get-started/agent-frameworks/mastra/page.mdx b/content/docs/get-started/agent-frameworks/mastra.mdx similarity index 96% rename from app/en/get-started/agent-frameworks/mastra/page.mdx rename to content/docs/get-started/agent-frameworks/mastra.mdx index f3d2fbe34..cac81a82b 100644 --- a/app/en/get-started/agent-frameworks/mastra/page.mdx +++ b/content/docs/get-started/agent-frameworks/mastra.mdx @@ -1,10 +1,9 @@ --- -title: "Build an AI Agent and Workflow with Arcade and Mastra" -description: "Create a TypeScript agent that uses Arcade tools to access Gmail and Slack" +title: Build an AI Agent and Workflow with Arcade and Mastra +sidebarTitle: Mastra +description: Create a TypeScript agent that uses Arcade tools to access Gmail and Slack --- -import { Steps, Tabs, Callout } from "nextra/components"; - [Mastra](https://mastra.ai/docs) is an open-source, TypeScript agent framework for building AI applications. It provides agents with memory, tool calling, workflows, and RAG capabilities. This guide uses **Mastra v1.x**. In this guide, you'll build an agent lets you read emails, send messages, and interact with Gmail and Slack using Arcade's MCP tools in a conversational interface with built-in authentication. You will also build a workflow that summarizes emails and sends them to Slack. @@ -108,7 +107,7 @@ These commands explicitly install `zod@3` because the Arcade SDK's `toZodToolSet Add your Arcade API key to **.env**: -```env filename=".env" +```env title=".env" ARCADE_API_KEY={arcade_api_key} ARCADE_USER_ID={arcade_user_id} ``` @@ -123,7 +122,7 @@ Create **src/mastra/tools/arcade.ts** to handle Arcade tool fetching and convers **Handling large tool outputs:** Tools like `Gmail.ListEmails` can return 200KB+ of email content. When the agent passes this data back to the LLM in the agentic loop, it may exceed token limits, resulting in rate limit errors. The code below includes output truncation to prevent this. </Callout> -```ts filename="src/mastra/tools/arcade.ts" +```ts title="src/mastra/tools/arcade.ts" import { Arcade } from "@arcadeai/arcadejs"; import { toZodToolSet, @@ -230,7 +229,7 @@ You might select your tools individually for a few reasons: * **Cost** Each tool's schema consumes tokens. Loading all Gmail tools (~20 tools) uses more tokens than loading just the 3 you need. This matters for rate limits and cost. <Callout type="info"> - Browse the [complete MCP server catalog](/resources/integrations) to see available servers and their tools. + Browse the [complete MCP server catalog](/en/resources/integrations) to see available servers and their tools. </Callout> ##### Arcade SDK functions @@ -248,7 +247,7 @@ You might select your tools individually for a few reasons: Create **src/mastra/agents/arcade.ts**: -```ts filename="src/mastra/agents/arcade.ts" +```ts title="src/mastra/agents/arcade.ts" import { Agent } from "@mastra/core/agent"; import { TokenLimiterProcessor, ToolCallFilter } from "@mastra/core/processors"; import { Memory } from "@mastra/memory"; @@ -305,7 +304,7 @@ IMPORTANT: When a tool returns an authorization response with a URL, tell the us Replace the contents of **src/mastra/index.ts** with the following to register your agent: -```ts filename="src/mastra/index.ts" +```ts title="src/mastra/index.ts" import { Mastra } from "@mastra/core"; import { arcadeAgent } from "./agents/arcade"; @@ -388,7 +387,7 @@ This also demonstrates how workflows: Create **src/mastra/workflows/email-digest.ts**: -```ts filename="src/mastra/workflows/email-digest.ts" +```ts title="src/mastra/workflows/email-digest.ts" import { createStep, createWorkflow } from "@mastra/core/workflows"; import { z } from "zod"; import { Arcade } from "@arcadeai/arcadejs"; @@ -589,7 +588,7 @@ export { emailDigestWorkflow }; Update **src/mastra/index.ts**: -```ts filename="src/mastra/index.ts" +```ts title="src/mastra/index.ts" import { Mastra } from "@mastra/core"; import { arcadeAgent } from "./agents/arcade"; import { emailDigestWorkflow } from "./workflows/email-digest"; @@ -625,12 +624,12 @@ export const mastra = new Mastra({ ## Next steps -- **Add more tools**: Browse the [tool catalog](/resources/integrations) and add tools for GitHub, Notion, Linear, and more. +- **Add more tools**: Browse the [tool catalog](/en/resources/integrations) and add tools for GitHub, Notion, Linear, and more. - **Schedule your workflow**: Use a cron job or [Mastra's scheduling](https://mastra.ai/docs/workflows/scheduling) to run your email digest every morning. - **Deploy to production**: Follow Mastra's [deployment guides](https://mastra.ai/docs/deployment/overview) to deploy your agent and workflows. <Callout type="info"> - **Building a multi-user app?** This tutorial uses a single `ARCADE_USER_ID` for simplicity. For production apps where each user needs their own OAuth tokens, see [Secure auth for production](/build/user-facing-agents/secure-auth-production) to learn how to dynamically pass user IDs and handle per-user authorization. + **Building a multi-user app?** This tutorial uses a single `ARCADE_USER_ID` for simplicity. For production apps where each user needs their own OAuth tokens, see [Secure auth for production](/en/build/user-facing-agents/secure-auth-production) to learn how to dynamically pass user IDs and handle per-user authorization. </Callout> ## Complete code @@ -638,7 +637,7 @@ export const mastra = new Mastra({ <details> <summary>**src/mastra/tools/arcade.ts** (full file)</summary> -```ts filename="src/mastra/tools/arcade.ts" +```ts title="src/mastra/tools/arcade.ts" import { Arcade } from "@arcadeai/arcadejs"; import { toZodToolSet, @@ -724,7 +723,7 @@ export async function getArcadeTools(userId: string) { <details> <summary>**src/mastra/agents/arcade.ts** (full file)</summary> -```ts filename="src/mastra/agents/arcade.ts" +```ts title="src/mastra/agents/arcade.ts" import { Agent } from "@mastra/core/agent"; import { TokenLimiterProcessor, ToolCallFilter } from "@mastra/core/processors"; import { Memory } from "@mastra/memory"; @@ -779,7 +778,7 @@ IMPORTANT: When a tool returns an authorization response with a URL, tell the us <details> <summary>**src/mastra/index.ts** (full file)</summary> -```ts filename="src/mastra/index.ts" +```ts title="src/mastra/index.ts" import { Mastra } from "@mastra/core"; import { arcadeAgent } from "./agents/arcade"; import { emailDigestWorkflow } from "./workflows/email-digest"; @@ -799,7 +798,7 @@ export const mastra = new Mastra({ <details> <summary>**src/mastra/workflows/email-digest.ts** (full file)</summary> -```ts filename="src/mastra/workflows/email-digest.ts" +```ts title="src/mastra/workflows/email-digest.ts" import { createStep, createWorkflow } from "@mastra/core/workflows"; import { z } from "zod"; import { Arcade } from "@arcadeai/arcadejs"; diff --git a/content/docs/get-started/agent-frameworks/meta.json b/content/docs/get-started/agent-frameworks/meta.json new file mode 100644 index 000000000..f576e6ac8 --- /dev/null +++ b/content/docs/get-started/agent-frameworks/meta.json @@ -0,0 +1,17 @@ +{ + "title": "Agent Frameworks", + "pages": [ + "index", + "setup-arcade-with-your-llm-python", + "ag2", + "copilotkit", + "crewai", + "google-adk", + "langchain", + "mastra", + "openai-agents", + "tanstack-ai", + "vercelai", + "springai" + ] +} diff --git a/content/docs/get-started/agent-frameworks/openai-agents/meta.json b/content/docs/get-started/agent-frameworks/openai-agents/meta.json new file mode 100644 index 000000000..3533f2b45 --- /dev/null +++ b/content/docs/get-started/agent-frameworks/openai-agents/meta.json @@ -0,0 +1,4 @@ +{ + "title": "OpenAI Agents", + "pages": ["overview", "setup-python", "setup-typescript"] +} diff --git a/app/en/get-started/agent-frameworks/openai-agents/overview/page.mdx b/content/docs/get-started/agent-frameworks/openai-agents/overview.mdx similarity index 53% rename from app/en/get-started/agent-frameworks/openai-agents/overview/page.mdx rename to content/docs/get-started/agent-frameworks/openai-agents/overview.mdx index 486489123..4b8177126 100644 --- a/app/en/get-started/agent-frameworks/openai-agents/overview/page.mdx +++ b/content/docs/get-started/agent-frameworks/openai-agents/overview.mdx @@ -1,18 +1,17 @@ --- -title: "Arcade with OpenAI Agents" -description: "Integrate Arcade tools with the OpenAI Agents SDK" +title: Arcade with OpenAI Agents +sidebarTitle: Overview +description: Integrate Arcade tools with the OpenAI Agents SDK --- -# Arcade with OpenAI Agents - The [OpenAI Agents SDK](https://openai.github.io/openai-agents-python/) provides a framework for building AI agents. Arcade integrates with both the Python and JavaScript versions, giving your agents access to Gmail, GitHub, Slack, and 7,500+ other tools. ## Get started Choose your language to set up Arcade with OpenAI Agents: -- **[Python setup](/get-started/agent-frameworks/openai-agents/setup-python)** - Build a CLI agent with Arcade tools using the `agents-arcade` package -- **[TypeScript setup](/get-started/agent-frameworks/openai-agents/setup-typescript)** - Build an agent with Arcade tools using `@arcadeai/arcadejs` +- **[Python setup](/en/get-started/agent-frameworks/openai-agents/setup-python)** - Build a CLI agent with Arcade tools using the `agents-arcade` package +- **[TypeScript setup](/en/get-started/agent-frameworks/openai-agents/setup-typescript)** - Build an agent with Arcade tools using `@arcadeai/arcadejs` ## What you can build @@ -24,4 +23,4 @@ With Arcade and OpenAI Agents, your agents can: - Search the web and extract content - Access 7,500+ other integrations -Browse the [full MCP server catalog](/resources/integrations) to see all available tools. +Browse the [full MCP server catalog](/en/resources/integrations) to see all available tools. diff --git a/app/en/get-started/agent-frameworks/openai-agents/setup-python/page.mdx b/content/docs/get-started/agent-frameworks/openai-agents/setup-python.mdx similarity index 97% rename from app/en/get-started/agent-frameworks/openai-agents/setup-python/page.mdx rename to content/docs/get-started/agent-frameworks/openai-agents/setup-python.mdx index 2f5029e65..0713fc9a2 100644 --- a/app/en/get-started/agent-frameworks/openai-agents/setup-python/page.mdx +++ b/content/docs/get-started/agent-frameworks/openai-agents/setup-python.mdx @@ -1,12 +1,9 @@ --- -title: "Setup Arcade with OpenAI Agents SDK" -description: "Learn how to use Arcade tools in OpenAI Agents applications" +title: Setup Arcade with OpenAI Agents SDK +sidebarTitle: Setup (Python) +description: Learn how to use Arcade tools in OpenAI Agents applications --- -import { Steps, Tabs, Callout } from "nextra/components"; - -# Setup Arcade with OpenAI Agents SDK - Learn how to integrate Arcade tools using OpenAI Agents primitives. The [OpenAI Agents SDK](https://openai.github.io/openai-agents-python/) is a popular Python library for building AI agents. It builds on top of the OpenAI API, and provides an interface for building agents. @@ -21,7 +18,7 @@ You will implement a CLI agent that can use Arcade tools to help the user with t <GuideOverview.Prerequisites> - <DashboardLink path="register" /> -- [Obtain an Arcade API key](/get-started/setup/api-keys) +- [Obtain an Arcade API key](/en/get-started/setup/api-keys) - The [`uv` package manager](https://docs.astral.sh/uv/) </GuideOverview.Prerequisites> @@ -80,7 +77,7 @@ uv add openai-agents arcadepy Create a new file called `.env` and add the following environment variables: -```env filename=".env" +```env title=".env" # Arcade API key ARCADE_API_KEY=YOUR_ARCADE_API_KEY # Arcade user ID (this is the email address you used to login to Arcade) @@ -93,7 +90,7 @@ OPENAI_API_KEY=YOUR_OPENAI_API_KEY Create a new file called `main.py` and add the following code: -```python filename="main.py" +```python title="main.py" from agents import Agent, Runner, TResponseInputItem from agents.run_context import RunContextWrapper from agents.tool import FunctionTool @@ -132,7 +129,7 @@ This includes many imports, here's a breakdown: These variables customize the agent and manage the tools in the rest of the code. Feel free to configure them to your liking. -```python filename="main.py" +```python title="main.py" # Load environment variables load_dotenv() @@ -156,7 +153,7 @@ Here, you define `ToolError` to handle errors from the Arcade tools. It wraps th You also define `convert_output_to_json` to convert the output of the Arcade tools to a JSON string. This is useful because the output of the Arcade tools is not always a JSON object, and the OpenAI Agents SDK expects a JSON string. -```python filename="main.py" +```python title="main.py" # Arcade to OpenAI agent exception classes class ToolError(AgentsException): def __init__(self, result: ExecuteToolResponse | str): @@ -193,7 +190,7 @@ This helper function implements "just in time" (JIT) tool authorization using Ar authorization-related traces, which reduces the risk of hallucinations. </Callout> -```python filename="main.py" +```python title="main.py" async def authorize_tool(client: AsyncArcade, context: RunContextWrapper, tool_name: str): if not context.context.get("user_id"): raise ToolError("No user ID and authorization required for tool") @@ -213,7 +210,7 @@ async def authorize_tool(client: AsyncArcade, context: RunContextWrapper, tool_n This helper function shows how the OpenAI Agents framework invokes the Arcade tools. It handles the authorization flow, and then calls the tool using the `execute` method. It handles the conversion of the arguments from JSON to a dictionary (expected by Arcade) and the conversion of the output from the Arcade tool to a JSON string (expected by the OpenAI Agents framework). Here is where you call the helper functions defined earlier to authorize the tool and convert the output to a JSON string. -```python filename="main.py" +```python title="main.py" async def invoke_arcade_tool( context: RunContextWrapper, tool_args: str, @@ -248,7 +245,7 @@ This helper function is long, here's a breakdown of what it does for clarity: - get the Arcade tools to OpenAI-formatted tools - create a list of FunctionTool objects, mapping each tool to a partial function that invokes the tool via the Arcade client. -```python filename="main.py" +```python title="main.py" async def get_arcade_tools( client: AsyncArcade | None = None, tools: list[str] | None = None, @@ -324,7 +321,7 @@ The loop is a while loop that captures the user input, appends it to the convers When a user interrupts the loop, the interruption handles via the helper function you wrote earlier. -```python filename="main.py" +```python title="main.py" async def main(): # Get tools from the configured MCP servers tools = await get_arcade_tools(mcp_servers=MCP_SERVERS, @@ -391,7 +388,7 @@ You should see the agent responding to your prompts like any model, as well as h The team provides example code for you to reference: -```python filename="main.py" +```python title="main.py" from agents import Agent, Runner, TResponseInputItem from agents.run_context import RunContextWrapper from agents.tool import FunctionTool diff --git a/app/en/get-started/agent-frameworks/openai-agents/setup-typescript/page.mdx b/content/docs/get-started/agent-frameworks/openai-agents/setup-typescript.mdx similarity index 93% rename from app/en/get-started/agent-frameworks/openai-agents/setup-typescript/page.mdx rename to content/docs/get-started/agent-frameworks/openai-agents/setup-typescript.mdx index e4bffad50..75a30dacd 100644 --- a/app/en/get-started/agent-frameworks/openai-agents/setup-typescript/page.mdx +++ b/content/docs/get-started/agent-frameworks/openai-agents/setup-typescript.mdx @@ -1,12 +1,9 @@ --- -title: "Setup Arcade with OpenAI Agents (TypeScript)" -description: "Build an agent with Arcade tools using the OpenAI Agents SDK for JavaScript/TypeScript" +title: Setup Arcade with OpenAI Agents (TypeScript) +sidebarTitle: Setup (TypeScript) +description: Build an agent with Arcade tools using the OpenAI Agents SDK for JavaScript/TypeScript --- -import { Steps, Tabs, Callout } from "nextra/components"; - -# Setup Arcade with OpenAI Agents (TypeScript) - The [OpenAI Agents SDK for JavaScript](https://openai.github.io/openai-agents-js/) provides a framework for building AI agents in TypeScript and JavaScript. Arcade's `@arcadeai/arcadejs` library converts Arcade tools to the format OpenAI Agents expects. <GuideOverview> @@ -19,7 +16,7 @@ Build an agent that uses Arcade tools to help users with Gmail and Slack <GuideOverview.Prerequisites> - <DashboardLink path="register" /> -- [Obtain an Arcade API key](/get-started/setup/api-keys) +- [Obtain an Arcade API key](/en/get-started/setup/api-keys) - [Node.js](https://nodejs.org/) 18+ (includes npm) or [Bun](https://bun.sh/) </GuideOverview.Prerequisites> @@ -75,7 +72,7 @@ bun add @openai/agents @arcadeai/arcadejs dotenv Create a `.env` file with your API keys: -```env filename=".env" +```env title=".env" # Arcade API key from https://app.arcade.dev/api-keys ARCADE_API_KEY=YOUR_ARCADE_API_KEY # Your Arcade user ID (the email you used to sign up) @@ -88,7 +85,7 @@ OPENAI_API_KEY=YOUR_OPENAI_API_KEY Create `index.ts` (or `index.mjs` for plain JavaScript): -```typescript filename="index.ts" +```typescript title="index.ts" import Arcade from "@arcadeai/arcadejs"; import { executeOrAuthorizeZodTool, toZod } from "@arcadeai/arcadejs/lib/index"; import { Agent, run, tool } from "@openai/agents"; @@ -107,7 +104,7 @@ const MODEL = "gpt-4o-mini"; Use `toZod()` to convert Arcade tools to Zod schemas, then `tool()` to convert them to OpenAI Agents format: -```typescript filename="index.ts" +```typescript title="index.ts" async function getArcadeTools(client: Arcade, userId: string) { // Fetch tools from MCP servers const mcpServerTools = await Promise.all( @@ -152,7 +149,7 @@ async function getArcadeTools(client: Arcade, userId: string) { ### Create and run the agent -```typescript filename="index.ts" +```typescript title="index.ts" async function main() { // Initialize Arcade client const client = new Arcade(); @@ -219,7 +216,7 @@ After the user visits the URL and authorizes, running the same request again wil For more control over authorization flow, you can create a custom execute factory: -```typescript filename="index.ts" +```typescript title="index.ts" import { isAuthorizationRequiredError, executeZodTool } from "@arcadeai/arcadejs/lib/index"; // Custom factory that waits for authorization @@ -294,7 +291,7 @@ bun run index.ts ## Complete code -```typescript filename="index.ts" +```typescript title="index.ts" import Arcade from "@arcadeai/arcadejs"; import { executeOrAuthorizeZodTool, toZod } from "@arcadeai/arcadejs/lib/index"; import { Agent, run, tool } from "@openai/agents"; @@ -395,5 +392,5 @@ main().catch(console.error); - Add more tools by modifying `MCP_SERVERS` and `INDIVIDUAL_TOOLS` - Build a web interface using frameworks like Next.js or Express -- See the [Vercel AI SDK tutorial](/get-started/agent-frameworks/vercelai) or [TanStack AI tutorial](/get-started/agent-frameworks/tanstack-ai) for complete web chatbot examples -- Explore [creating custom tools](/build/create-tools/tool-basics/build-mcp-server) with the Arcade Tool SDK +- See the [Vercel AI SDK tutorial](/en/get-started/agent-frameworks/vercelai) or [TanStack AI tutorial](/en/get-started/agent-frameworks/tanstack-ai) for complete web chatbot examples +- Explore [creating custom tools](/en/build/create-tools/tool-basics/build-mcp-server) with the Arcade Tool SDK diff --git a/app/en/get-started/agent-frameworks/setup-arcade-with-your-llm-python/page.mdx b/content/docs/get-started/agent-frameworks/setup-arcade-with-your-llm-python.mdx similarity index 94% rename from app/en/get-started/agent-frameworks/setup-arcade-with-your-llm-python/page.mdx rename to content/docs/get-started/agent-frameworks/setup-arcade-with-your-llm-python.mdx index 48638e64d..69a5302da 100644 --- a/app/en/get-started/agent-frameworks/setup-arcade-with-your-llm-python/page.mdx +++ b/content/docs/get-started/agent-frameworks/setup-arcade-with-your-llm-python.mdx @@ -1,12 +1,11 @@ --- -title: "Connect Arcade to LLM (Python)" -description: "Learn how to connect Arcade to your LLM in Python" +title: Connect Arcade to your LLM +metaTitle: Connect Arcade to LLM (Python) +sidebarTitle: Setup Arcade with your LLM (Python) +description: Learn how to connect Arcade to your LLM in Python --- -import { Steps, Tabs, Callout } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; - -# Connect Arcade to your LLM +import { SignupLink } from "@/components/analytics"; Arcade tools work alongside an LLM. To make that work, you need a small piece of glue code called a "harness." The harness orchestrates the back-and-forth between the user, the model, and the tools. In this guide, you'll build one so you can wire Arcade into your LLM-powered app. @@ -20,7 +19,7 @@ Integrate Arcade's tool-calling capabilities into an application that uses an LL <GuideOverview.Prerequisites> - An <SignupLink linkLocation="docs:call-tools-directly">Arcade account</SignupLink> -- An [Arcade API key](/get-started/setup/api-keys) +- An [Arcade API key](/en/get-started/setup/api-keys) - An [OpenRouter API key](https://openrouter.ai/docs/guides/overview/auth/provisioning-api-keys) - The [`uv` package manager](https://docs.astral.sh/uv/getting-started/installation/) @@ -95,7 +94,7 @@ arcade-llm-example/ Create a new file called `.env` and add your Arcade API key, as well as your OpenAI API key: -```text filename=".env" +```text title=".env" ARCADE_API_KEY=YOUR_ARCADE_API_KEY ARCADE_USER_ID=YOUR_ARCADE_USER_ID OPENROUTER_API_KEY=YOUR_OPENROUTER_API_KEY @@ -106,7 +105,7 @@ OPENROUTER_MODEL=YOUR_OPENROUTER_MODEL The `ARCADE_USER_ID` is the email address you used to sign up for Arcade. When your app is ready for production, you can set this dynamically based on your app's auth system. Learn more about how to achieve secure auth in production - [here](/build/user-facing-agents/secure-auth-production). + [here](/en/build/user-facing-agents/secure-auth-production). </Callout> <Callout type="info"> @@ -128,7 +127,7 @@ If you don't know which model to use, try one of these: Open the `main.py` file in your editor of choice, and replace the contents with the following: -```python filename="main.py" +```python title="main.py" from arcadepy import Arcade from openai import OpenAI from dotenv import load_dotenv @@ -150,7 +149,7 @@ llm_client = OpenAI( In this example, you're implementing a multi-tool agent that can retrieve and send emails, as well as send messages to Slack. While a harness can expose a broad catalog of tools to the LLM, it's best to limit that set to what's relevant for the task to keep the model efficient. -```python filename="main.py" +```python title="main.py" # Define the tools for the agent to use tool_catalog = [ "Gmail.ListEmails", @@ -169,7 +168,7 @@ for tool in tool_catalog: The model can use any tool you give it, and some tools require permission before they work. When this happens, you can either involve the model in the permission step or handle it behind the scenes and continue as if the tool were already authorized. In this guide, authorization happens outside the model so it can act as if the tool is already available. It's like ordering a coffee: after you place your order, the barista handles payment behind the counter instead of explaining every step of card verification and receipts. The customer (and the model) gets the result without having to think about any of the intermediate steps. -```python filename="main.py" +```python title="main.py" # Helper function to authorize and run any tool def authorize_and_run_tool(tool_name: str, input: str): # Start the authorization process @@ -206,7 +205,7 @@ There are many orchestration patterns that can be used to handle the LLM invocat To avoid the risk of infinite loops, limit the number of turns (in this case, a maximum of 5). This is a parameter that you can tune to your needs. Set it to a value that is high enough to allow the LLM to complete its task but low enough to prevent infinite loops. -```python filename="main.py" +```python title="main.py" def invoke_llm( history: list[dict], model: str = "google/gemini-2.5-flash", @@ -271,7 +270,7 @@ Now that you've written the helper functions, write an agentic loop that interac 3. Invoke the LLM with the conversation history, tools, and tool choice 4. Repeat from step 2 until the user decides to stop the conversation -```python filename="main.py" +```python title="main.py" def chat(): """Interactive multi-turn chat session.""" print("Chat started. Type 'quit' or 'exit' to end the session.\n") @@ -329,14 +328,14 @@ With the selection of tools above, you should be able to get the agent to effect ## Next Steps -- Learn more about using Arcade with a [framework](/get-started/agent-frameworks) or [MCP client](/get-started/mcp-clients). -- Learn more about how to [build your own MCP Servers](/build/create-tools/tool-basics/build-mcp-server). +- Learn more about using Arcade with a [framework](/en/get-started/agent-frameworks) or [MCP client](/en/get-started/mcp-clients). +- Learn more about how to [build your own MCP Servers](/en/build/create-tools/tool-basics/build-mcp-server). ## Example code <details> <summary>**main.py** (full file)</summary> -```python filename="main.py" +```python title="main.py" from arcadepy import Arcade from dotenv import load_dotenv from openai import OpenAI diff --git a/app/en/get-started/agent-frameworks/springai/page.mdx b/content/docs/get-started/agent-frameworks/springai.mdx similarity index 96% rename from app/en/get-started/agent-frameworks/springai/page.mdx rename to content/docs/get-started/agent-frameworks/springai.mdx index 54e279f0c..6ac766d88 100644 --- a/app/en/get-started/agent-frameworks/springai/page.mdx +++ b/content/docs/get-started/agent-frameworks/springai.mdx @@ -1,12 +1,10 @@ --- -title: "Build an AI Agent with Arcade and Spring AI" -description: "Learn how to call Arcade tools from Spring AI." +title: Build an AI agent with Arcade and Spring AI +metaTitle: Build an AI Agent with Arcade and Spring AI +sidebarTitle: Spring AI SDK +description: Learn how to call Arcade tools from Spring AI. --- -import { Steps, Tabs, Callout } from "nextra/components"; - -# Build an AI agent with Arcade and Spring AI - [Spring AI](https://docs.spring.io/spring-ai/reference/) is a framework that brings AI capabilities to Spring Boot applications. It provides a unified API for interacting with large language models, including support for tool calling. This guide uses **Spring AI 1.1.x** with **Spring Boot 3.5**. In this guide, you'll build a Spring Boot web application that uses Arcade's Gmail and Slack tools through Spring AI's `ChatClient`. Your application exposes a REST endpoint that accepts natural language prompts, lets the LLM decide which tools to call, and handles Arcade's authorization flow when users need to connect their accounts. @@ -77,7 +75,7 @@ Add the `arcade-spring-boot-starter` to your project. This starter auto-configur Add the dependency to your `pom.xml`: -```xml filename="pom.xml" +```xml title="pom.xml" <dependency> <groupId>dev.arcade</groupId> <artifactId>arcade-spring-boot-starter</artifactId> @@ -91,7 +89,7 @@ Add the dependency to your `pom.xml`: Add the dependency to your `build.gradle`: -```groovy filename="build.gradle" +```groovy title="build.gradle" implementation 'dev.arcade:arcade-spring-boot-starter:${arcade-java-version}' ``` @@ -105,7 +103,7 @@ See the [Arcade Java SDK docs](https://github.com/ArcadeAI/arcade-java#installat Replace the contents of `src/main/resources/application.properties` with the following: -```properties filename="application.properties" +```properties title="application.properties" spring.application.name=arcade-agent # OpenAI @@ -135,7 +133,7 @@ Do not commit API keys to version control. Use environment variables or a secret Create `src/main/java/com/example/arcadeagent/ArcadeToolProvider.java`. This service wraps Arcade tool calls and exposes them to Spring AI using `@Tool` annotations: -```java filename="src/main/java/com/example/arcadeagent/ArcadeToolProvider.java" +```java title="src/main/java/com/example/arcadeagent/ArcadeToolProvider.java" package com.example.arcadeagent; import dev.arcade.client.ArcadeClient; @@ -323,7 +321,7 @@ public class ArcadeToolProvider { Create `src/main/java/com/example/arcadeagent/ChatController.java`. This controller exposes a REST endpoint that accepts user prompts and returns AI responses: -```java filename="src/main/java/com/example/arcadeagent/ChatController.java" +```java title="src/main/java/com/example/arcadeagent/ChatController.java" package com.example.arcadeagent; import org.springframework.ai.chat.client.ChatClient; @@ -442,8 +440,8 @@ curl -X POST http://localhost:8080/api/chat \ ## Next steps -1. **Add more tools**: Browse the [MCP server catalog](/resources/integrations) and add `@Tool` methods for GitHub, Google Docs, Notion, and more. -2. **Add user authentication**: In production, resolve `userId` from your authentication system instead of a static configuration property. See [Secure auth in production](/build/user-facing-agents/secure-auth-production) for best practices. +1. **Add more tools**: Browse the [MCP server catalog](/en/resources/integrations) and add `@Tool` methods for GitHub, Google Docs, Notion, and more. +2. **Add user authentication**: In production, resolve `userId` from your authentication system instead of a static configuration property. See [Secure auth in production](/en/build/user-facing-agents/secure-auth-production) for best practices. 3. **Stream responses**: Replace `.call()` with `.stream()` on the `ChatClient` to stream responses using [Server-Sent Events](https://docs.spring.io/spring-ai/reference/api/chatclient.html#_streaming_responses). ## Complete code @@ -451,7 +449,7 @@ curl -X POST http://localhost:8080/api/chat \ <details> <summary>**pom.xml** (full file)</summary> -```xml filename="pom.xml" +```xml title="pom.xml" <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" @@ -529,7 +527,7 @@ curl -X POST http://localhost:8080/api/chat \ <details> <summary>**src/main/resources/application.properties** (full file)</summary> -```properties filename="src/main/resources/application.properties" +```properties title="src/main/resources/application.properties" spring.application.name=arcade-agent # OpenAI @@ -546,7 +544,7 @@ arcade.user-id=${ARCADE_USER_ID} <details> <summary>**src/main/java/com/example/arcadeagent/ArcadeToolProvider.java** (full file)</summary> -```java filename="src/main/java/com/example/arcadeagent/ArcadeToolProvider.java" +```java title="src/main/java/com/example/arcadeagent/ArcadeToolProvider.java" package com.example.arcadeagent; import dev.arcade.client.ArcadeClient; @@ -699,7 +697,7 @@ public class ArcadeToolProvider { <details> <summary>**src/main/java/com/example/arcadeagent/ChatController.java** (full file)</summary> -```java filename="src/main/java/com/example/arcadeagent/ChatController.java" +```java title="src/main/java/com/example/arcadeagent/ChatController.java" package com.example.arcadeagent; import org.springframework.ai.chat.client.ChatClient; diff --git a/app/en/get-started/agent-frameworks/tanstack-ai/page.mdx b/content/docs/get-started/agent-frameworks/tanstack-ai.mdx similarity index 97% rename from app/en/get-started/agent-frameworks/tanstack-ai/page.mdx rename to content/docs/get-started/agent-frameworks/tanstack-ai.mdx index 6c321733f..1a3f7e58c 100644 --- a/app/en/get-started/agent-frameworks/tanstack-ai/page.mdx +++ b/content/docs/get-started/agent-frameworks/tanstack-ai.mdx @@ -1,12 +1,9 @@ --- -title: "Build an AI Chatbot with Arcade and TanStack AI" -description: "Create a browser-based chatbot that uses Arcade tools to access Gmail and Slack" +title: Build an AI Chatbot with Arcade and TanStack AI +sidebarTitle: TanStack AI +description: Create a browser-based chatbot that uses Arcade tools to access Gmail and Slack --- -import { Steps, Tabs, Callout } from "nextra/components"; - -# Build an AI Chatbot with Arcade and TanStack AI - [TanStack AI](https://tanstack.com/ai/latest/docs) is a type-safe, provider-agnostic SDK for building AI applications in JavaScript and TypeScript. It provides streaming responses, tool calling, and framework-agnostic primitives for React, Solid, and vanilla JavaScript. Provider adapters let you switch between OpenAI, Anthropic, Google Gemini, and Ollama without rewriting your code. In this guide, you'll build a browser-based chatbot using [TanStack Start](https://tanstack.com/start) that uses Arcade's Gmail and Slack tools. Your users can read emails, send messages, and interact with Slack through a conversational interface with built-in authentication. @@ -137,7 +134,7 @@ bun add -D @tailwindcss/typography Then add the typography plugin to your **src/styles.css**: -```css filename="src/styles.css" +```css title="src/styles.css" @import "tailwindcss"; @plugin "@tailwindcss/typography"; ``` @@ -146,7 +143,7 @@ Then add the typography plugin to your **src/styles.css**: Create a **.env** file with your API keys: -```env filename=".env" +```env title=".env" ARCADE_API_KEY={arcade_api_key} ARCADE_USER_ID={arcade_user_id} OPENAI_API_KEY=your_openai_api_key @@ -158,7 +155,7 @@ The `ARCADE_USER_ID` is your app's internal identifier for the user (often the e Create the directory and file **src/routes/api/chat.ts**. This server route handles chat requests and streams AI responses: -```ts filename="src/routes/api/chat.ts" +```ts title="src/routes/api/chat.ts" import { createFileRoute } from "@tanstack/react-router"; import { toServerSentEventsResponse, chat, toolDefinition } from "@tanstack/ai"; import type { JSONSchema } from "@tanstack/ai"; @@ -355,7 +352,7 @@ export const Route = createFileRoute("/api/chat")({ <Callout type="info"> You can mix MCP servers (which give you all their tools) with individual - tools. Browse the [complete MCP server catalog](/resources/integrations) to + tools. Browse the [complete MCP server catalog](/en/resources/integrations) to see what's available. </Callout> @@ -367,7 +364,7 @@ export const Route = createFileRoute("/api/chat")({ Create **src/functions/auth.ts** to check authorization status: -```ts filename="src/functions/auth.ts" +```ts title="src/functions/auth.ts" import { createServerFn } from "@tanstack/react-start"; import { Arcade } from "@arcadeai/arcadejs"; @@ -396,7 +393,7 @@ This server function allows the frontend to poll for authorization completion, c Update **src/routes/index.tsx** with the chat interface. TanStack AI's `useChat` hook manages messages, streaming, and loading states: -```tsx filename="src/routes/index.tsx" +```tsx title="src/routes/index.tsx" import { createFileRoute } from "@tanstack/react-router"; import { useChat, fetchServerSentEvents } from "@tanstack/ai-react"; import { useState, useRef, useEffect } from "react"; @@ -695,9 +692,9 @@ On first use, you'll see an authorization button. Click it to connect your Gmail ## Next steps -1. **Add more tools**: Browse the [MCP server catalog](/resources/integrations) and add tools for GitHub, Notion, Linear, and more. +1. **Add more tools**: Browse the [MCP server catalog](/en/resources/integrations) and add tools for GitHub, Notion, Linear, and more. 2. **Try different providers**: Swap `@tanstack/ai-openai` for `@tanstack/ai-anthropic` or `@tanstack/ai-gemini` to use different AI models. -3. **Add user authentication**: In production, get `userId` from your auth system instead of environment variables. See [Secure your server](/build/create-tools/secure-your-server) for best practices. +3. **Add user authentication**: In production, get `userId` from your auth system instead of environment variables. See [Secure your server](/en/build/create-tools/secure-your-server) for best practices. 4. **Deploy your chatbot**: TanStack Start supports deployment to Vercel, Netlify, Cloudflare, and Node.js servers. ## Complete code @@ -705,7 +702,7 @@ On first use, you'll see an authorization button. Click it to connect your Gmail <details> <summary>**src/routes/api/chat.ts** (full file)</summary> -```ts filename="src/routes/api/chat.ts" +```ts title="src/routes/api/chat.ts" import { createFileRoute } from "@tanstack/react-router"; import { toServerSentEventsResponse, chat, toolDefinition } from "@tanstack/ai"; import type { JSONSchema } from "@tanstack/ai"; @@ -882,7 +879,7 @@ export const Route = createFileRoute("/api/chat")({ <details> <summary>**src/functions/auth.ts** (full file)</summary> -```ts filename="src/functions/auth.ts" +```ts title="src/functions/auth.ts" import { createServerFn } from "@tanstack/react-start"; import { Arcade } from "@arcadeai/arcadejs"; @@ -910,7 +907,7 @@ export const checkAuthStatus = createServerFn({ method: "POST" }) <details> <summary>**src/routes/index.tsx** (full file)</summary> -```tsx filename="src/routes/index.tsx" +```tsx title="src/routes/index.tsx" import { createFileRoute } from "@tanstack/react-router"; import { useChat, fetchServerSentEvents } from "@tanstack/ai-react"; import { useState, useRef, useEffect } from "react"; diff --git a/app/en/get-started/agent-frameworks/vercelai/page.mdx b/content/docs/get-started/agent-frameworks/vercelai.mdx similarity index 96% rename from app/en/get-started/agent-frameworks/vercelai/page.mdx rename to content/docs/get-started/agent-frameworks/vercelai.mdx index 1008a51ad..8446fe6ad 100644 --- a/app/en/get-started/agent-frameworks/vercelai/page.mdx +++ b/content/docs/get-started/agent-frameworks/vercelai.mdx @@ -1,14 +1,11 @@ --- -title: "Build an AI Chatbot with Arcade and Vercel AI SDK" -description: "Create a browser-based chatbot that uses Arcade tools to access Gmail and Slack" +title: Build an AI Chatbot with Arcade and Vercel AI SDK +sidebarTitle: Vercel AI SDK +description: Create a browser-based chatbot that uses Arcade tools to access Gmail and Slack --- -import { Steps, Tabs, Callout } from "nextra/components"; - {/* Editorial: Structure - Removed marketing language "straightforward" in opening paragraph; Voice and tone - Changed "Let's get started!" to more direct language; Structure - Modified intro to better follow 10/20/70 format */} -# Build an AI Chatbot with Arcade and Vercel AI SDK - The [Vercel AI SDK](https://sdk.vercel.ai/) is a TypeScript toolkit for building AI-powered applications. It provides streaming responses, framework-agnostic support for React, Next.js, Vue, and more, plus AI provider switching. This guide uses **Vercel AI SDK v6**. In this guide, you'll build a browser-based chatbot that uses Arcade's Gmail and Slack tools. Your users can read emails, send messages, and interact with Slack through a conversational interface with built-in authentication. @@ -112,7 +109,7 @@ Follow the AI Elements prompts to complete the installation. Create a **.env.local** file with your API keys: -```env filename=".env.local" +```env title=".env.local" ARCADE_API_KEY={arcade_api_key} ARCADE_USER_ID={arcade_user_id} OPENAI_API_KEY=your_openai_api_key @@ -124,7 +121,7 @@ The `ARCADE_USER_ID` is your app's internal identifier for the user (often the e Create **app/api/chat/route.ts**. Start with the imports: -```ts filename="app/api/chat/route.ts" +```ts title="app/api/chat/route.ts" import { openai } from "@ai-sdk/openai"; import { streamText, convertToModelMessages, stepCountIs } from "ai"; import { Arcade } from "@arcadeai/arcadejs"; @@ -147,7 +144,7 @@ import { Define which MCP servers and individual tools your chatbot can access: -```ts filename="app/api/chat/route.ts" +```ts title="app/api/chat/route.ts" const config = { // Get all tools from these MCP servers mcpServers: ["Slack"], @@ -171,7 +168,7 @@ IMPORTANT: When calling tools, if an argument is optional, do not set it. Never <Callout type="info"> You can mix MCP servers (which give you all their tools) with individual - tools. Browse the [complete MCP server catalog](/resources/integrations) to + tools. Browse the [complete MCP server catalog](/en/resources/integrations) to see what's available. </Callout> @@ -179,7 +176,7 @@ IMPORTANT: When calling tools, if an argument is optional, do not set it. Never This function retrieves tools from Arcade and converts them to Vercel AI SDK format. The `toVercelTools` adapter converts Arcade's tool format to match what the Vercel AI SDK expects, and `stripNullValues` prevents issues with optional parameters: -```ts filename="app/api/chat/route.ts" +```ts title="app/api/chat/route.ts" // Strip null and undefined values from tool inputs // Some LLMs send null for optional params, which can cause tool failures function stripNullValues( @@ -256,7 +253,7 @@ The `executeOrAuthorizeZodTool` factory is key here. It automatically handles au Handle incoming chat requests by streaming AI responses with tools: -```ts filename="app/api/chat/route.ts" +```ts title="app/api/chat/route.ts" export async function POST(req: Request) { try { const { messages } = await req.json(); @@ -289,7 +286,7 @@ The `stopWhen: stepCountIs(5)` allows the AI to make multiple tool calls in a si To detect when a user completes OAuth authorization, create **app/api/auth/status/route.ts**: -```ts filename="app/api/auth/status/route.ts" +```ts title="app/api/auth/status/route.ts" import { Arcade } from "@arcadeai/arcadejs"; export async function POST(req: Request) { @@ -324,7 +321,7 @@ This endpoint allows the frontend to poll for authorization completion, creating AI Elements provides pre-built components for conversations, messages, and input. All you need to add is custom handling for Arcade's OAuth flow. Replace the contents of **app/page.tsx** with the following code: -```tsx filename="app/page.tsx" +```tsx title="app/page.tsx" "use client"; import { useChat } from "@ai-sdk/react"; @@ -435,7 +432,7 @@ The `AuthPendingUI` component polls for OAuth completion and calls `onAuthComple The `Conversation` component handles auto-scrolling, `Message` handles role-based styling, and [`MessageResponse` renders markdown automatically](https://ai-sdk.dev/elements/components/message#features). The implementation checks for Arcade's `authorization_required` flag in tool results: -```tsx filename="app/page.tsx" +```tsx title="app/page.tsx" export default function Chat() { const { messages, sendMessage, regenerate, status } = useChat(); const isLoading = status === "submitted" || status === "streaming"; @@ -596,8 +593,8 @@ On first use, you'll see an authorization button. Click it to connect your Gmail ## Next steps -1. **Add more tools**: Browse the [MCP server catalog](/resources/integrations) and add tools for GitHub, Notion, Linear, and more. -2. **Add user authentication**: In production, get `userId` from your auth system instead of environment variables. See [Secure your server](/build/create-tools/secure-your-server) for best practices. +1. **Add more tools**: Browse the [MCP server catalog](/en/resources/integrations) and add tools for GitHub, Notion, Linear, and more. +2. **Add user authentication**: In production, get `userId` from your auth system instead of environment variables. See [Secure your server](/en/build/create-tools/secure-your-server) for best practices. 3. **Deploy to Vercel**: Push your chatbot to GitHub and [deploy to Vercel](https://vercel.com/docs/deployments/overview) with one click. Add your environment variables in the Vercel dashboard. ## Complete code @@ -605,7 +602,7 @@ On first use, you'll see an authorization button. Click it to connect your Gmail <details> <summary>**app/api/chat/route.ts** (full file)</summary> -```ts filename="app/api/chat/route.ts" +```ts title="app/api/chat/route.ts" import { openai } from "@ai-sdk/openai"; import { streamText, convertToModelMessages, stepCountIs } from "ai"; import { Arcade } from "@arcadeai/arcadejs"; @@ -723,7 +720,7 @@ export async function POST(req: Request) { <details> <summary>**app/api/auth/status/route.ts** (full file)</summary> -```ts filename="app/api/auth/status/route.ts" +```ts title="app/api/auth/status/route.ts" import { Arcade } from "@arcadeai/arcadejs"; export async function POST(req: Request) { @@ -757,7 +754,7 @@ export async function POST(req: Request) { <details> <summary>**app/page.tsx** (full file)</summary> -```tsx filename="app/page.tsx" +```tsx title="app/page.tsx" "use client"; import { useChat } from "@ai-sdk/react"; diff --git a/app/en/get-started/agent-plugin/page.mdx b/content/docs/get-started/agent-plugin/index.mdx similarity index 82% rename from app/en/get-started/agent-plugin/page.mdx rename to content/docs/get-started/agent-plugin/index.mdx index 84c84bd0b..0478b5cc1 100644 --- a/app/en/get-started/agent-plugin/page.mdx +++ b/content/docs/get-started/agent-plugin/index.mdx @@ -1,12 +1,10 @@ --- -title: "Install the Arcade agent plugin" -description: "Install Arcade in Cursor, Claude Code, VS Code, GitHub Copilot CLI, Codex, or Claude Desktop. Connect your agent to apps through Arcade with browser sign-in." +title: Install the Arcade agent plugin +sidebarTitle: Install +description: Install Arcade in Cursor, Claude Code, VS Code, GitHub Copilot CLI, Codex, or Claude Desktop. Connect your agent to apps through Arcade with browser sign-in. --- -import { Tabs } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; - -# Install the Arcade agent plugin +import { SignupLink } from "@/components/analytics"; For agent developers, the Arcade plugin connects your coding agent or desktop client to apps through Arcade's hosted gateway. Sign in with your Arcade account. You don't need an Arcade API key or a gateway configuration. @@ -19,7 +17,7 @@ Install the Arcade agent plugin in a supported client and connect to its hosted <GuideOverview.Prerequisites> -- A [supported client](/get-started/agent-plugin/whats-included#client-support): Cursor, Claude Code, VS Code, GitHub Copilot CLI, Codex, or Claude Desktop +- A [supported client](/en/get-started/agent-plugin/whats-included#client-support): Cursor, Claude Code, VS Code, GitHub Copilot CLI, Codex, or Claude Desktop - An <SignupLink linkLocation="docs:agent-plugin-install">Arcade account</SignupLink> - For Claude Code, Claude Cowork, Cursor CLI, GitHub Copilot CLI, and VS Code Copilot sessions: [Node.js](https://nodejs.org/) on the host's `PATH` @@ -168,15 +166,15 @@ Your client must support remote HTTP MCP servers and browser sign-in. This conne ## Connect apps -[Gateway sign-in and app authorization are separate](/learn/server-level-vs-tool-level-auth). After connecting the gateway during installation, ask your agent to use an app. If the app needs authorization, follow the browser sign-in link and approve the required permissions. +[Gateway sign-in and app authorization are separate](/en/learn/server-level-vs-tool-level-auth). After connecting the gateway during installation, ask your agent to use an app. If the app needs authorization, follow the browser sign-in link and approve the required permissions. Return to your agent after sign-in and continue the task. To manage connections, ask "Show my connected apps" or "Connect Google." -Some tools require a provider API key or another secret. See [default-project setup](/get-started/agent-plugin/whats-included#projects-and-secrets) before using those tools. +Some tools require a provider API key or another secret. See [default-project setup](/en/get-started/agent-plugin/whats-included#projects-and-secrets) before using those tools. ## Next steps -- [What's included](/get-started/agent-plugin/whats-included): plugin components and client support -- [Connect to MCP clients](/get-started/mcp-clients): configure a client against a gateway you control -- [Integrations catalog](/resources/integrations): every app and toolkit Arcade supports -- [Agentic development](/get-started/setup/connect-arcade-docs): point your agent at these docs with `llms.txt` +- [What's included](/en/get-started/agent-plugin/whats-included): plugin components and client support +- [Connect to MCP clients](/en/get-started/mcp-clients): configure a client against a gateway you control +- [Integrations catalog](/en/resources/integrations): every app and toolkit Arcade supports +- [Agentic development](/en/get-started/setup/connect-arcade-docs): point your agent at these docs with `llms.txt` diff --git a/content/docs/get-started/agent-plugin/meta.json b/content/docs/get-started/agent-plugin/meta.json new file mode 100644 index 000000000..0983b81d5 --- /dev/null +++ b/content/docs/get-started/agent-plugin/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Agent Plugin", + "pages": ["index", "whats-included"] +} diff --git a/app/en/get-started/agent-plugin/whats-included/page.mdx b/content/docs/get-started/agent-plugin/whats-included.mdx similarity index 90% rename from app/en/get-started/agent-plugin/whats-included/page.mdx rename to content/docs/get-started/agent-plugin/whats-included.mdx index a4c8f68ed..aa73aebca 100644 --- a/app/en/get-started/agent-plugin/whats-included/page.mdx +++ b/content/docs/get-started/agent-plugin/whats-included.mdx @@ -1,12 +1,8 @@ --- -title: "What's included" -description: "Components bundled with the Arcade agent plugin and support across clients." +title: What's included +description: Components bundled with the Arcade agent plugin and support across clients. --- -import { Code, Table } from "nextra/components"; - -# What's included - The Arcade plugin provides tools and workflows to connect to external services via Arcade's library of over 1,000 optimized tools. Component support within the plugin varies by client support. ## MCP server @@ -132,7 +128,7 @@ Lists the apps you have connected and lets you disconnect one. The plugin uses the default project created when you sign up for Arcade. Selecting another project in the Dashboard, or joining another project via invite, **does not** change the plugin's project context. -Individual tools may require a provider API key or another secret. Add these to your default project's [secrets](/build/create-tools/tool-basics/create-tool-secrets#store-your-secret). Secrets configured in another project are not available to you through the plugin. +Individual tools may require a provider API key or another secret. Add these to your default project's [secrets](/en/build/create-tools/tool-basics/create-tool-secrets#store-your-secret). Secrets configured in another project are not available to you through the plugin. ## Example requests @@ -146,5 +142,5 @@ Or ask "What can Arcade do?" and pick a workflow from the answer. ## Next steps -- [Install the Arcade agent plugin](/get-started/agent-plugin): client-native setup and gateway sign-in -- [Integrations catalog](/resources/integrations): supported apps and toolkits +- [Install the Arcade agent plugin](/en/get-started/agent-plugin): client-native setup and gateway sign-in +- [Integrations catalog](/en/resources/integrations): supported apps and toolkits diff --git a/app/en/get-started/mcp-clients/claude-code/page.mdx b/content/docs/get-started/mcp-clients/claude-code.mdx similarity index 67% rename from app/en/get-started/mcp-clients/claude-code/page.mdx rename to content/docs/get-started/mcp-clients/claude-code.mdx index a8f816244..f9618e4f5 100644 --- a/app/en/get-started/mcp-clients/claude-code/page.mdx +++ b/content/docs/get-started/mcp-clients/claude-code.mdx @@ -1,10 +1,12 @@ -import { Steps, Callout } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; +--- +title: Use Arcade in Claude Code +sidebarTitle: Claude Code +--- -# Use Arcade in Claude Code +import { SignupLink } from "@/components/analytics"; <Callout type="info"> - To use Arcade's hosted gateway, [install the Arcade agent plugin](/get-started/agent-plugin). + To use Arcade's hosted gateway, [install the Arcade agent plugin](/en/get-started/agent-plugin). This guide connects your client to a gateway you configure. </Callout> @@ -18,13 +20,13 @@ Connect Claude Code to an Arcade MCP Gateway. <GuideOverview.Prerequisites> 1. Create an <SignupLink linkLocation="docs:claude-code-client">Arcade account</SignupLink> -2. Create an [Arcade MCP Gateway](/operate/governance/mcp-gateways) and select the tools you want to use +2. Create an [Arcade MCP Gateway](/en/operate/governance/mcp-gateways) and select the tools you want to use </GuideOverview.Prerequisites> </GuideOverview> <Callout type="info"> - **Production end users with their own identities?** If your end users already sign in to Entra ID, Okta, Auth0, Clerk, or another OIDC provider, configure a [User Source](/operate/identity/user-sources) on your gateway. Arcade redirects each end user to your identity provider on sign-in and identifies them by an OIDC subject claim. + **Production end users with their own identities?** If your end users already sign in to Entra ID, Okta, Auth0, Clerk, or another OIDC provider, configure a [User Source](/en/operate/identity/user-sources) on your gateway. Arcade redirects each end user to your identity provider on sign-in and identifies them by an OIDC subject claim. </Callout> <Steps> diff --git a/app/en/get-started/mcp-clients/claude-desktop/page.mdx b/content/docs/get-started/mcp-clients/claude-desktop.mdx similarity index 90% rename from app/en/get-started/mcp-clients/claude-desktop/page.mdx rename to content/docs/get-started/mcp-clients/claude-desktop.mdx index a1eb3b5d3..3729463c9 100644 --- a/app/en/get-started/mcp-clients/claude-desktop/page.mdx +++ b/content/docs/get-started/mcp-clients/claude-desktop.mdx @@ -1,5 +1,9 @@ -import { Steps, Callout } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; +--- +title: Use Arcade in Claude Desktop +sidebarTitle: Claude Desktop +--- + +import { SignupLink } from "@/components/analytics"; import Image from "next/image"; export const IMAGE_SCALE_FACTOR = 2; @@ -34,10 +38,8 @@ export const STEP_7_DARK_HEIGHT = 2458; export const STEP_7_LIGHT_WIDTH = 1848; export const STEP_7_LIGHT_HEIGHT = 2450; -# Use Arcade in Claude Desktop - <Callout type="info"> - To use Arcade's hosted gateway, [install the Arcade agent plugin](/get-started/agent-plugin). + To use Arcade's hosted gateway, [install the Arcade agent plugin](/en/get-started/agent-plugin). This guide connects your client to a gateway you configure. </Callout> @@ -51,8 +53,8 @@ Connect Claude Desktop to an Arcade MCP Gateway. <GuideOverview.Prerequisites> 1. Create an <SignupLink linkLocation="docs:claude-desktop-client">Arcade account</SignupLink> -2. Get an [Arcade API key](/get-started/setup/api-keys) -3. Create an [Arcade MCP Gateway](/operate/governance/mcp-gateways) and select the tools you want to use +2. Get an [Arcade API key](/en/get-started/setup/api-keys) +3. Create an [Arcade MCP Gateway](/en/operate/governance/mcp-gateways) and select the tools you want to use </GuideOverview.Prerequisites> @@ -65,7 +67,7 @@ Connect Claude Desktop to an Arcade MCP Gateway. </Callout> <Callout type="info"> - **Production end users with their own identities?** If your end users already sign in to Entra ID, Okta, Auth0, Clerk, or another OIDC provider, configure a [User Source](/operate/identity/user-sources) on your gateway. Arcade redirects each end user to your identity provider on sign-in and identifies them by an OIDC subject claim. + **Production end users with their own identities?** If your end users already sign in to Entra ID, Okta, Auth0, Clerk, or another OIDC provider, configure a [User Source](/en/operate/identity/user-sources) on your gateway. Arcade redirects each end user to your identity provider on sign-in and identifies them by an OIDC subject claim. </Callout> <Steps> @@ -138,7 +140,7 @@ Claude checks the gateway URL and shows **Authentication** and **OAuth client** - **Sign in now** for Authentication - **Use Claude's published identity** for OAuth client -Those defaults match Arcade Auth and [User Sources](/operate/identity/user-sources). Do not switch to **Sign in when needed** or **No sign-in**. Those options skip or delay the OAuth flow Arcade uses. Do not switch to **Register automatically** or **Use your own OAuth client**. Arcade already supports Claude's published identity (CIMD). Leave **Request headers** empty. Leave **Advanced** collapsed. +Those defaults match Arcade Auth and [User Sources](/en/operate/identity/user-sources). Do not switch to **Sign in when needed** or **No sign-in**. Those options skip or delay the OAuth flow Arcade uses. Do not switch to **Register automatically** or **Use your own OAuth client**. Arcade already supports Claude's published identity (CIMD). Leave **Request headers** empty. Leave **Advanced** collapsed. Then click **Add**. diff --git a/app/en/get-started/mcp-clients/copilot-studio/page.mdx b/content/docs/get-started/mcp-clients/copilot-studio.mdx similarity index 87% rename from app/en/get-started/mcp-clients/copilot-studio/page.mdx rename to content/docs/get-started/mcp-clients/copilot-studio.mdx index b227fc468..5d297dd44 100644 --- a/app/en/get-started/mcp-clients/copilot-studio/page.mdx +++ b/content/docs/get-started/mcp-clients/copilot-studio.mdx @@ -1,5 +1,9 @@ -import { Steps, Callout } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; +--- +title: Use Arcade in Microsoft Copilot Studio +sidebarTitle: Microsoft Copilot Studio +--- + +import { SignupLink } from "@/components/analytics"; import Image from "next/image"; export const IMAGE_SCALE_FACTOR = 2; @@ -12,8 +16,6 @@ export const STEP_3_HEIGHT = 1074; export const STEP_4_WIDTH = 1512; export const STEP_4_HEIGHT = 790; -# Use Arcade in Microsoft Copilot Studio - <GuideOverview> <GuideOverview.Outcomes> @@ -25,15 +27,15 @@ Connect Microsoft Copilot Studio to an Arcade MCP Gateway. 1. A Microsoft 365 subscription with access to Copilot Studio 2. Create an <SignupLink linkLocation="docs:copilot-studio-client">Arcade account</SignupLink> -3. Get an [Arcade API key](/get-started/setup/api-keys) -4. Create an [Arcade MCP Gateway](/operate/governance/mcp-gateways) and select the tools you want to use +3. Get an [Arcade API key](/en/get-started/setup/api-keys) +4. Create an [Arcade MCP Gateway](/en/operate/governance/mcp-gateways) and select the tools you want to use </GuideOverview.Prerequisites> </GuideOverview> <Callout type="info"> - **Production end users with their own identities?** If your end users already sign in to Entra ID, Okta, Auth0, Clerk, or another OIDC provider, configure a [User Source](/operate/identity/user-sources) on your gateway. Arcade redirects each end user to your identity provider on sign-in and identifies them by an OIDC subject claim. + **Production end users with their own identities?** If your end users already sign in to Entra ID, Okta, Auth0, Clerk, or another OIDC provider, configure a [User Source](/en/operate/identity/user-sources) on your gateway. Arcade redirects each end user to your identity provider on sign-in and identifies them by an OIDC subject claim. </Callout> <Steps> diff --git a/app/en/get-started/mcp-clients/cursor/page.mdx b/content/docs/get-started/mcp-clients/cursor.mdx similarity index 57% rename from app/en/get-started/mcp-clients/cursor/page.mdx rename to content/docs/get-started/mcp-clients/cursor.mdx index e98080087..e296b992d 100644 --- a/app/en/get-started/mcp-clients/cursor/page.mdx +++ b/content/docs/get-started/mcp-clients/cursor.mdx @@ -1,19 +1,17 @@ --- -title: "Use Arcade in Cursor" -description: "Connect Cursor to an Arcade MCP Gateway over Streamable HTTP and pick which tools Cursor can use." +title: Use Arcade in Cursor +sidebarTitle: Cursor +description: Connect Cursor to an Arcade MCP Gateway over Streamable HTTP and pick which tools Cursor can use. --- -import { Steps, Callout, Tabs } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; - -# Use Arcade in Cursor +import { SignupLink } from "@/components/analytics"; <Callout type="info"> - To use Arcade's hosted gateway, [install the Arcade agent plugin](/get-started/agent-plugin). + To use Arcade's hosted gateway, [install the Arcade agent plugin](/en/get-started/agent-plugin). This guide connects your client to a gateway you configure. </Callout> -Cursor is an MCP client. Point it at an Arcade [MCP Gateway](/operate/governance/mcp-gateways) URL over the Streamable HTTP transport and Cursor's agent can call any tool you selected on that gateway — GitHub, Linear, Gmail, Slack, or any other toolkit from the [Arcade integrations catalog](/resources/integrations), plus tools from your own [custom MCP servers](/build/create-tools/tool-basics/build-mcp-server). +Cursor is an MCP client. Point it at an Arcade [MCP Gateway](/en/operate/governance/mcp-gateways) URL over the Streamable HTTP transport and Cursor's agent can call any tool you selected on that gateway — GitHub, Linear, Gmail, Slack, or any other toolkit from the [Arcade integrations catalog](/en/resources/integrations), plus tools from your own [custom MCP servers](/en/build/create-tools/tool-basics/build-mcp-server). <GuideOverview> <GuideOverview.Outcomes> @@ -25,8 +23,8 @@ Connect Cursor to an Arcade MCP Gateway. <GuideOverview.Prerequisites> 1. Create an <SignupLink linkLocation="docs:claude-desktop-client">Arcade account</SignupLink> -2. Get an [Arcade API key](/get-started/setup/api-keys) -3. Create an [Arcade MCP Gateway](/operate/governance/mcp-gateways) and select the tools you want to use +2. Get an [Arcade API key](/en/get-started/setup/api-keys) +3. Create an [Arcade MCP Gateway](/en/operate/governance/mcp-gateways) and select the tools you want to use </GuideOverview.Prerequisites> @@ -39,7 +37,7 @@ Connect Cursor to an Arcade MCP Gateway. </Callout> <Callout type="info"> - **Production end users with their own identities?** If your end users already sign in to Entra ID, Okta, Auth0, Clerk, or another OIDC provider, configure a [User Source](/operate/identity/user-sources) on your gateway. Arcade redirects each end user to your identity provider on sign-in and identifies them by an OIDC subject claim. + **Production end users with their own identities?** If your end users already sign in to Entra ID, Okta, Auth0, Clerk, or another OIDC provider, configure a [User Source](/en/operate/identity/user-sources) on your gateway. Arcade redirects each end user to your identity provider on sign-in and identifies them by an OIDC subject claim. </Callout> ### Set up Cursor @@ -90,6 +88,6 @@ Cursor will open the MCP settings file, and you can add a new entry to the `mcpS ## Pick which tools Cursor sees -A gateway is the filter that decides which tools Cursor can call. On the [MCP Gateways dashboard](https://api.arcade.dev/dashboard/mcp-gateways), open your gateway's **Allowed Tools** picker and search or filter across every server in scope — Arcade-hosted toolkits, [remote MCP servers](/operate/governance/remote-mcp-servers) you registered, and your own [Arcade-deployed MCP servers](/build/arcade-deploy). Save the gateway and Cursor immediately sees the new tool list on its next request; no restart or client-side config change is needed. +A gateway is the filter that decides which tools Cursor can call. On the [MCP Gateways dashboard](https://api.arcade.dev/dashboard/mcp-gateways), open your gateway's **Allowed Tools** picker and search or filter across every server in scope — Arcade-hosted toolkits, [remote MCP servers](/en/operate/governance/remote-mcp-servers) you registered, and your own [Arcade-deployed MCP servers](/en/build/arcade-deploy). Save the gateway and Cursor immediately sees the new tool list on its next request; no restart or client-side config change is needed. -Popular starting points for a Cursor gateway: GitHub, Linear, Gmail, Slack, Google Calendar, Notion, Jira, Confluence — plus any [remote MCP server](/operate/governance/remote-mcp-servers) or [custom MCP server](/build/create-tools/tool-basics/build-mcp-server) you have registered. Browse the full [integrations catalog](/resources/integrations) for the rest. +Popular starting points for a Cursor gateway: GitHub, Linear, Gmail, Slack, Google Calendar, Notion, Jira, Confluence — plus any [remote MCP server](/en/operate/governance/remote-mcp-servers) or [custom MCP server](/en/build/create-tools/tool-basics/build-mcp-server) you have registered. Browse the full [integrations catalog](/en/resources/integrations) for the rest. diff --git a/app/en/get-started/mcp-clients/github-copilot/page.mdx b/content/docs/get-started/mcp-clients/github-copilot.mdx similarity index 93% rename from app/en/get-started/mcp-clients/github-copilot/page.mdx rename to content/docs/get-started/mcp-clients/github-copilot.mdx index e5d23c119..61240cc81 100644 --- a/app/en/get-started/mcp-clients/github-copilot/page.mdx +++ b/content/docs/get-started/mcp-clients/github-copilot.mdx @@ -1,5 +1,9 @@ -import { Steps, Callout, Tabs } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; +--- +title: Use Arcade in GitHub Copilot +sidebarTitle: GitHub Copilot +--- + +import { SignupLink } from "@/components/analytics"; import Image from "next/image"; export const IMAGE_SCALE_FACTOR = 2; @@ -18,10 +22,8 @@ export const VISUAL_STUDIO_STEP_5_HEIGHT = 362; export const VISUAL_STUDIO_STEP_6_WIDTH = 1632; export const VISUAL_STUDIO_STEP_6_HEIGHT = 360; -# Use Arcade in GitHub Copilot - <Callout type="info"> - To use Arcade's hosted gateway, [install the Arcade agent plugin](/get-started/agent-plugin). + To use Arcade's hosted gateway, [install the Arcade agent plugin](/en/get-started/agent-plugin). This guide connects your client to a gateway you configure. </Callout> @@ -35,15 +37,15 @@ Connect GitHub Copilot to an Arcade MCP Gateway. <GuideOverview.Prerequisites> 1. Create an <SignupLink linkLocation="docs:github-copilot-client">Arcade account</SignupLink> -2. Get an [Arcade API key](/get-started/setup/api-keys) -3. Create an [Arcade MCP Gateway](/operate/governance/mcp-gateways) and select the tools you want to use +2. Get an [Arcade API key](/en/get-started/setup/api-keys) +3. Create an [Arcade MCP Gateway](/en/operate/governance/mcp-gateways) and select the tools you want to use </GuideOverview.Prerequisites> </GuideOverview> <Callout type="info"> - **Production end users with their own identities?** If your end users already sign in to Entra ID, Okta, Auth0, Clerk, or another OIDC provider, configure a [User Source](/operate/identity/user-sources) on your gateway. Arcade redirects each end user to your identity provider on sign-in and identifies them by an OIDC subject claim. + **Production end users with their own identities?** If your end users already sign in to Entra ID, Okta, Auth0, Clerk, or another OIDC provider, configure a [User Source](/en/operate/identity/user-sources) on your gateway. Arcade redirects each end user to your identity provider on sign-in and identifies them by an OIDC subject claim. </Callout> ### Set up GitHub Copilot @@ -55,7 +57,7 @@ Connect GitHub Copilot to an Arcade MCP Gateway. <Tabs.Tab> In VS Code, Github Copilot will automatically detect the configured MCP servers. Read the [VS Code - instructions](/get-started/mcp-clients/visual-studio-code) to set both of + instructions](/en/get-started/mcp-clients/visual-studio-code) to set both of them up. </Tabs.Tab> <Tabs.Tab> diff --git a/content/docs/get-started/mcp-clients/index.mdx b/content/docs/get-started/mcp-clients/index.mdx new file mode 100644 index 000000000..5f25a7a05 --- /dev/null +++ b/content/docs/get-started/mcp-clients/index.mdx @@ -0,0 +1,17 @@ +--- +title: Connect to MCP Clients +sidebarTitle: Overview +description: Learn how to connect Cursor, Claude Desktop, Claude Code, VS Code, GitHub Copilot, and Copilot Studio to Arcade MCP Gateways over Streamable HTTP. +--- + +import { MCPClientGrid } from "@/components/pages/get-started/mcp-clients/mcp-client-grid"; + +You can connect [Arcade MCP servers](/en/resources/integrations) to MCP-compatible clients and development environments to unlock powerful flows for your agents. + +Every supported MCP client, including Cursor, Claude Desktop, Claude Code, VS Code, GitHub Copilot, and Copilot Studio, connects to an Arcade [MCP Gateway](/en/operate/governance/mcp-gateways) over the Streamable HTTP transport. The gateway decides which tools the client can call: pick from Arcade-hosted toolkits (GitHub, Linear, Gmail, Slack, Google Calendar, Notion, Jira, Confluence, and dozens more in the [integrations catalog](/en/resources/integrations)), from [remote MCP servers](/en/operate/governance/remote-mcp-servers) you registered, and from your own [custom MCP servers](/en/build/create-tools/tool-basics/build-mcp-server) hosted with [Arcade Deploy](/en/build/arcade-deploy). + +<MCPClientGrid /> + +## Use the hosted gateway + +To connect your agent to apps through Arcade's hosted gateway, [install the Arcade agent plugin](/en/get-started/agent-plugin). This guide connects your client to a gateway you configure. diff --git a/content/docs/get-started/mcp-clients/meta.json b/content/docs/get-started/mcp-clients/meta.json new file mode 100644 index 000000000..46d3408d7 --- /dev/null +++ b/content/docs/get-started/mcp-clients/meta.json @@ -0,0 +1,12 @@ +{ + "title": "MCP Clients", + "pages": [ + "index", + "cursor", + "claude-desktop", + "claude-code", + "visual-studio-code", + "copilot-studio", + "github-copilot" + ] +} diff --git a/app/en/get-started/mcp-clients/visual-studio-code/page.mdx b/content/docs/get-started/mcp-clients/visual-studio-code.mdx similarity index 83% rename from app/en/get-started/mcp-clients/visual-studio-code/page.mdx rename to content/docs/get-started/mcp-clients/visual-studio-code.mdx index dfb1b49d6..4e14afc6c 100644 --- a/app/en/get-started/mcp-clients/visual-studio-code/page.mdx +++ b/content/docs/get-started/mcp-clients/visual-studio-code.mdx @@ -1,17 +1,19 @@ -import { Steps, Callout } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; +--- +title: Use Arcade in Visual Studio Code +sidebarTitle: Visual Studio Code +--- -# Use Arcade in Visual Studio Code +import { SignupLink } from "@/components/analytics"; In this guide, you'll learn how to connect Visual Studio Code to an Arcade MCP Gateway. <Callout type="info"> - To use Arcade's hosted gateway, [install the Arcade agent plugin](/get-started/agent-plugin). + To use Arcade's hosted gateway, [install the Arcade agent plugin](/en/get-started/agent-plugin). This guide connects your client to a gateway you configure. </Callout> <Callout type="info"> - **Production end users with their own identities?** If your end users already sign in to Entra ID, Okta, Auth0, Clerk, or another OIDC provider, configure a [User Source](/operate/identity/user-sources) on your gateway. Arcade redirects each end user to your identity provider on sign-in and identifies them by an OIDC subject claim. + **Production end users with their own identities?** If your end users already sign in to Entra ID, Okta, Auth0, Clerk, or another OIDC provider, configure a [User Source](/en/operate/identity/user-sources) on your gateway. Arcade redirects each end user to your identity provider on sign-in and identifies them by an OIDC subject claim. </Callout> <Steps> @@ -19,7 +21,7 @@ In this guide, you'll learn how to connect Visual Studio Code to an Arcade MCP G ### Prerequisites 1. Create an <SignupLink linkLocation="docs:claude-desktop-client">Arcade account</SignupLink> -2. Create an [Arcade MCP Gateway](/operate/governance/mcp-gateways) and select the tools you want to use +2. Create an [Arcade MCP Gateway](/en/operate/governance/mcp-gateways) and select the tools you want to use ### Set up Visual Studio Code diff --git a/content/docs/get-started/meta.json b/content/docs/get-started/meta.json new file mode 100644 index 000000000..eab68acd6 --- /dev/null +++ b/content/docs/get-started/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Get Started", + "icon": "BookOpen", + "pages": [ + "about-arcade", + "agent-plugin", + "setup", + "quickstarts", + "agent-frameworks", + "mcp-clients" + ] +} diff --git a/app/en/get-started/quickstarts/call-tool-agent/page.mdx b/content/docs/get-started/quickstarts/call-tool-agent.mdx similarity index 96% rename from app/en/get-started/quickstarts/call-tool-agent/page.mdx rename to content/docs/get-started/quickstarts/call-tool-agent.mdx index dbcab0061..5f17e998c 100644 --- a/app/en/get-started/quickstarts/call-tool-agent/page.mdx +++ b/content/docs/get-started/quickstarts/call-tool-agent.mdx @@ -1,13 +1,12 @@ --- -title: "Calling tools in your agent" -description: "Learn how to call tools in your agent" +title: Calling tools in your agent with Arcade +metaTitle: Calling tools in your agent +sidebarTitle: Call tools in agents +description: Learn how to call tools in your agent --- -import { Steps, Tabs, Callout } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; -import { AgentFrameworkTabs } from "@/app/_components/agent-framework-tabs"; - -# Calling tools in your agent with Arcade +import { SignupLink } from "@/components/analytics"; +import { AgentFrameworkTabs } from "@/components/agent-framework-tabs"; Arcade gives your AI agents the power to act. With Arcade-hosted tools, your AI-powered apps can send Gmail, update Notion, message in Slack, and more. @@ -21,7 +20,7 @@ Install and use the Arcade client to call Arcade Hosted Tools. <GuideOverview.Prerequisites> - An <SignupLink linkLocation="docs:call-tools-directly" utmCampaign="docs-quickstart-call-tool-agent">Arcade account</SignupLink> -- An [Arcade API key](/get-started/setup/api-keys) +- An [Arcade API key](/en/get-started/setup/api-keys) - The [`uv` package manager](https://docs.astral.sh/uv/getting-started/installation/) if you are using Python - The [`bun` runtime](https://bun.com/) if you are using TypeScript - [Apache Maven](https://maven.apache.org/) or [Gradle](https://gradle.org/) if you are using Java @@ -102,13 +101,13 @@ Create a new Maven or Gradle project with the following dependency: #### Gradle: -```groovy filename="build.gradle.kts" +```groovy title="build.gradle.kts" implementation("dev.arcade:arcade-java:${arcade-java-version}") ``` #### Maven: -```xml filename="pom.xml" +```xml title="pom.xml" <dependency> <groupId>dev.arcade</groupId> <artifactId>arcade-java</artifactId> @@ -129,7 +128,7 @@ See the [Arcade Java SDK docs](https://github.com/ArcadeAI/arcade-java?tab=readm Open the `main.py` file and replace the content with the following: -```python filename="main.py" +```python title="main.py" from arcadepy import Arcade # You can also set the `ARCADE_API_KEY` environment variable instead of passing it as a parameter. @@ -146,7 +145,7 @@ user_id = "{arcade_user_id}" Create a new script called `example.ts`: -```typescript filename="example.ts" +```typescript title="example.ts" import Arcade from "@arcadeai/arcadejs"; // You can also set the `ARCADE_API_KEY` environment variable instead of passing it as a parameter. @@ -181,7 +180,7 @@ This helper function will check if a tool requires authorization and if so, it w <Tabs.Tab> -```python filename="main.py" +```python title="main.py" # Helper function to authorize and run any tool def authorize_and_run_tool(tool_name, input, user_id): # Start the authorization process @@ -204,7 +203,7 @@ def authorize_and_run_tool(tool_name, input, user_id): <Tabs.Tab> -```typescript filename="example.ts" +```typescript title="example.ts" // Helper function to authorize and run any tool async function authorize_and_run_tool({ tool_name, @@ -242,7 +241,7 @@ async function authorize_and_run_tool({ </Tabs.Tab> <Tabs.Tab> -```java filename="App.java" +```java title="App.java" public static Map<String, JsonValue> authorizeAndRunTool(ArcadeClient client, String toolName, Map<String, Object> input, @@ -295,7 +294,7 @@ In this example workflow, we: <Tabs.Tab> -```python filename="main.py" +```python title="main.py" # This tool does not require authorization, so it will return the results # without prompting the user to authorize the tool call. response_search = authorize_and_run_tool( @@ -351,7 +350,7 @@ print(response_send_email.output.value) <Tabs.Tab> -```typescript filename="example.ts" +```typescript title="example.ts" // This tool does not require authorization, so it will return the results // without prompting the user to authorize the app. const response_search = await authorize_and_run_tool({ @@ -409,7 +408,7 @@ console.log(respose_send_email.output?.value); </Tabs.Tab> <Tabs.Tab> -```java filename="App.java" +```java title="App.java" Map<String, JsonValue> searchResult = authorizeAndRunTool( client, "GoogleNews.SearchNewsStories", @@ -539,7 +538,7 @@ logger.info( ## Next Steps -In this example, we call the tool methods directly. In your real applications and agents, you'll likely be letting the LLM decide which tools to call. Learn more about using Arcade with Frameworks in the [Frameworks](/get-started/agent-frameworks) section, or [how to build your own tools](/build/create-tools/tool-basics/build-mcp-server). +In this example, we call the tool methods directly. In your real applications and agents, you'll likely be letting the LLM decide which tools to call. Learn more about using Arcade with Frameworks in the [Frameworks](/en/get-started/agent-frameworks) section, or [how to build your own tools](/en/build/create-tools/tool-basics/build-mcp-server). <AgentFrameworkTabs /> @@ -551,7 +550,7 @@ In this example, we call the tool methods directly. In your real applications an <details> <summary>**main.py** (full file)</summary> -```python filename="example.py" +```python title="example.py" from arcadepy import Arcade # You can also set the `ARCADE_API_KEY` environment variable instead of passing it as a parameter. @@ -637,7 +636,7 @@ print(response_send_email.output.value) <details> <summary>**example.ts** (full file)</summary> -```typescript filename="example.ts" +```typescript title="example.ts" import Arcade from "@arcadeai/arcadejs"; // You can also set the `ARCADE_API_KEY` environment variable instead of passing it as a parameter. diff --git a/app/en/get-started/quickstarts/call-tool-client/page.mdx b/content/docs/get-started/quickstarts/call-tool-client.mdx similarity index 89% rename from app/en/get-started/quickstarts/call-tool-client/page.mdx rename to content/docs/get-started/quickstarts/call-tool-client.mdx index a4e68ee48..669941d87 100644 --- a/app/en/get-started/quickstarts/call-tool-client/page.mdx +++ b/content/docs/get-started/quickstarts/call-tool-client.mdx @@ -1,12 +1,12 @@ --- -title: "Call a tool in your IDE/MCP Client" -description: "Learn how to call a tool in your IDE/MCP Client" +title: Call a tool in your IDE/MCP Client +sidebarTitle: Call tools in IDE/MCP clients +description: Learn how to call a tool in your IDE/MCP Client --- -import { Steps, Tabs, Callout } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; +import { SignupLink } from "@/components/analytics"; import Image from "next/image"; -import { MCPClientGrid } from "@/app/en/get-started/mcp-clients/mcp-client-grid"; +import { MCPClientGrid } from "@/components/pages/get-started/mcp-clients/mcp-client-grid"; export const IMAGE_SCALE_FACTOR = 2; export const CREATE_MCP_GATEWAY_DARK_WIDTH = 921; @@ -22,8 +22,6 @@ export const MCP_GATEWAY_URL_DARK_HEIGHT = 498; export const MCP_GATEWAY_URL_LIGHT_WIDTH = 2834; export const MCP_GATEWAY_URL_LIGHT_HEIGHT = 498; -# Call a tool in your IDE/MCP Client - Tools enable your AI agents to perform actions on your behalf. For specific workflows and use cases, this may involve calling tools from multiple MCP servers. Arcade facilitates this by allowing you to create MCP Gateways to federate the tools from multiple MCP servers into a single collection for convenient management, control, and access. For example, if your agent specializes in solving specific tickets in Linear, you may want to use tools from the GitHub, Slack and Linear servers in your agent. These add up to 88 tools, which could be overwhelming for an LLM to use effectively. An MCP Gateway lets you pick the tools for this workflow, and with Tool Recommendation on, your agent requests only the tools each task needs instead of loading all of them up front. You can connect the gateway to any MCP client, making it possible to port your agent to multiple platforms and IDEs, and even share it with other users. <GuideOverview> @@ -106,7 +104,7 @@ Once you've selected the tools you want to include in the gateway, click the "Us ### Keep Tool Recommendations on -**Leave the "Tool Recommendations" card on.** It's on by default for new gateways in organizations with Tool Recommendation. Leave it on for a broad set of tools or varied tasks. For a small, fixed tool set, you can turn it off to give your agent the tool definitions directly. See [Tool Recommendation](/operate/governance/mcp-gateways/tool-recommendation) for how it works. +**Leave the "Tool Recommendations" card on.** It's on by default for new gateways in organizations with Tool Recommendation. Leave it on for a broad set of tools or varied tasks. For a small, fixed tool set, you can turn it off to give your agent the tool definitions directly. See [Tool Recommendation](/en/operate/governance/mcp-gateways/tool-recommendation) for how it works. Click the "Create MCP Gateway" button to create the gateway. @@ -158,9 +156,9 @@ As you interact with the agent, it will call the tools from the MCP Gateway. Wit ## Next Steps -- Learn more about [MCP Gateways](/operate/governance/mcp-gateways). -- Learn how [Tool Recommendation](/operate/governance/mcp-gateways/tool-recommendation) works and when to turn it off. +- Learn more about [MCP Gateways](/en/operate/governance/mcp-gateways). +- Learn how [Tool Recommendation](/en/operate/governance/mcp-gateways/tool-recommendation) works and when to turn it off. - Learn how to use MCP Gateways with: - - [Cursor](/get-started/mcp-clients/cursor) - - [Visual Studio Code](/get-started/mcp-clients/visual-studio-code) -- Build your own MCP servers with [arcade-mcp](/get-started/quickstarts/mcp-server-quickstart). + - [Cursor](/en/get-started/mcp-clients/cursor) + - [Visual Studio Code](/en/get-started/mcp-clients/visual-studio-code) +- Build your own MCP servers with [arcade-mcp](/en/get-started/quickstarts/mcp-server-quickstart). diff --git a/app/en/get-started/quickstarts/mcp-server-quickstart/page.mdx b/content/docs/get-started/quickstarts/mcp-server-quickstart.mdx similarity index 91% rename from app/en/get-started/quickstarts/mcp-server-quickstart/page.mdx rename to content/docs/get-started/quickstarts/mcp-server-quickstart.mdx index 4e04d7c56..c8fd57c90 100644 --- a/app/en/get-started/quickstarts/mcp-server-quickstart/page.mdx +++ b/content/docs/get-started/quickstarts/mcp-server-quickstart.mdx @@ -1,13 +1,11 @@ --- -title: "Build MCP Server QuickStart" -description: "Create your custom MCP Server with Arcade MCP" +title: Build MCP Server QuickStart +sidebarTitle: Build an MCP server for custom tools +description: Create your custom MCP Server with Arcade MCP --- -import { Steps, Tabs, Callout } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; -import { GuideOverview } from "@/app/_components/guide-overview"; - -# Build MCP Server QuickStart +import { SignupLink } from "@/components/analytics"; +import { GuideOverview } from "@/components/guide-overview"; <GuideOverview> <GuideOverview.Outcomes> @@ -108,7 +106,7 @@ Secrets are sensitive strings like passwords, API keys, or other tokens that gra <Tabs.Tab> You can create a `.env` file at your project root directory and add your secret: -```env filename=".env" +```env title=".env" MY_SECRET_KEY="my-secret-value" ``` @@ -186,7 +184,7 @@ uv run server.py stdio a subprocess. Because of this, the server may run in a different environment and not have access to secrets defined in your local `.env` file. Please refer to the [create a tool with - secrets](/build/create-tools/tool-basics/create-tool-secrets) guide for more + secrets](/en/build/create-tools/tool-basics/create-tool-secrets) guide for more information. </Callout> @@ -206,8 +204,8 @@ For HTTP transport, view your server's API docs at [http://127.0.0.1:8000/docs]( API key and secrets in your MCP connection settings. Otherwise, if you intend to expose your HTTP MCP server to the public internet with tool-level authorization and secrets, please follow the [deploying to the cloud with - Arcade Deploy](/build/arcade-deploy) guide or the [on-prem - MCP server](/operate/deploy/on-prem) guide for secure remote + Arcade Deploy](/en/build/arcade-deploy) guide or the [on-prem + MCP server](/en/operate/deploy/on-prem) guide for secure remote deployment. </Callout> @@ -294,8 +292,8 @@ Ensure you have set the environment variable in your terminal or `.env` file, an ## Next Steps -- **Learn how to write a tool with auth**: [Create a tool with auth](/build/create-tools/tool-basics/create-tool-auth) -- **Learn how to write a tool with secrets**: [Create a tool with secrets](/build/create-tools/tool-basics/create-tool-secrets) -- **Learn more about the Context object**: [Tools and Context](/build/create-tools/tool-basics/runtime-data-access) -- **Learn how to write tool evaluations**: [Create an evaluation suite](/build/create-tools/evaluate-tools/create-evaluation-suite) to optimize them for LLM usage -- **Learn how to deploy your MCP server**: [Deploy your MCP server](/build/arcade-deploy) +- **Learn how to write a tool with auth**: [Create a tool with auth](/en/build/create-tools/tool-basics/create-tool-auth) +- **Learn how to write a tool with secrets**: [Create a tool with secrets](/en/build/create-tools/tool-basics/create-tool-secrets) +- **Learn more about the Context object**: [Tools and Context](/en/build/create-tools/tool-basics/runtime-data-access) +- **Learn how to write tool evaluations**: [Create an evaluation suite](/en/build/create-tools/evaluate-tools/create-evaluation-suite) to optimize them for LLM usage +- **Learn how to deploy your MCP server**: [Deploy your MCP server](/en/build/arcade-deploy) diff --git a/content/docs/get-started/quickstarts/meta.json b/content/docs/get-started/quickstarts/meta.json new file mode 100644 index 000000000..a19460ab3 --- /dev/null +++ b/content/docs/get-started/quickstarts/meta.json @@ -0,0 +1,9 @@ +{ + "title": "Quickstarts", + "pages": [ + "call-tool-agent", + "call-tool-client", + "mcp-server-quickstart", + "[Set up Arcade for your org](/operate/quickstart)" + ] +} diff --git a/app/en/get-started/setup/api-keys/page.mdx b/content/docs/get-started/setup/api-keys.mdx similarity index 85% rename from app/en/get-started/setup/api-keys/page.mdx rename to content/docs/get-started/setup/api-keys.mdx index 88132cea6..b40404a94 100644 --- a/app/en/get-started/setup/api-keys/page.mdx +++ b/content/docs/get-started/setup/api-keys.mdx @@ -1,12 +1,10 @@ --- -title: "Getting Your API Key" -description: "Learn how to obtain and manage your Arcade API key" +title: Getting Your API Key +sidebarTitle: Get an API key +description: Learn how to obtain and manage your Arcade API key --- -import { Steps, Tabs, Callout } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; - -# Getting Your API Key +import { SignupLink } from "@/components/analytics"; Before you begin, you'll need an Arcade account - if you haven't created one yet, you can <SignupLink linkLocation="docs:get-api-key">sign up here</SignupLink>. Once you have an account, you can generate API keys through either the dashboard or CLI. @@ -106,5 +104,5 @@ The CLI will automatically: Once you have your API key, you can: -- [Start using tools](/get-started/quickstarts/call-tool-agent) -- [Create custom tools](/build/create-tools/tool-basics/build-mcp-server) +- [Start using tools](/en/get-started/quickstarts/call-tool-agent) +- [Create custom tools](/en/build/create-tools/tool-basics/build-mcp-server) diff --git a/app/en/get-started/setup/connect-arcade-docs/page.mdx b/content/docs/get-started/setup/connect-arcade-docs.mdx similarity index 90% rename from app/en/get-started/setup/connect-arcade-docs/page.mdx rename to content/docs/get-started/setup/connect-arcade-docs.mdx index 3fa170e18..f044ec216 100644 --- a/app/en/get-started/setup/connect-arcade-docs/page.mdx +++ b/content/docs/get-started/setup/connect-arcade-docs.mdx @@ -1,10 +1,9 @@ --- -title: "Agentic development" -description: "Learn how to speed up your development with agents in your IDEs" +title: Agentic development +sidebarTitle: Connect Arcade docs to your IDE +description: Learn how to speed up your development with agents in your IDEs --- -# Agentic development - Every page on the Arcade docs site serves as markdown. When an AI agent or coding assistant visits any docs URL, the site automatically returns `Content-Type: text/markdown` instead of HTML if: - The request `User-Agent` header matches a known AI agent (Claude, ChatGPT, Cursor, etc.) diff --git a/content/docs/get-started/setup/meta.json b/content/docs/get-started/setup/meta.json new file mode 100644 index 000000000..6bbe1df11 --- /dev/null +++ b/content/docs/get-started/setup/meta.json @@ -0,0 +1,9 @@ +{ + "title": "Setup", + "pages": [ + "api-keys", + "strong-authentication", + "connect-arcade-docs", + "windows-environment" + ] +} diff --git a/app/en/get-started/setup/strong-authentication/page.mdx b/content/docs/get-started/setup/strong-authentication.mdx similarity index 90% rename from app/en/get-started/setup/strong-authentication/page.mdx rename to content/docs/get-started/setup/strong-authentication.mdx index 3f41599d4..8bc52337a 100644 --- a/app/en/get-started/setup/strong-authentication/page.mdx +++ b/content/docs/get-started/setup/strong-authentication.mdx @@ -1,12 +1,9 @@ --- -title: "Set up strong authentication" -description: "Add a passkey, an authenticator app, or a security key to your Arcade Cloud account so you can sign in when your organization requires strong authentication." +title: Set up strong authentication +description: Add a passkey, an authenticator app, or a security key to your Arcade Cloud account so you can sign in when your organization requires strong authentication. --- -import { Callout } from "nextra/components"; -import { ContactEmail } from "@/app/_components/contact-email"; - -# Set up strong authentication +import { ContactEmail } from "@/components/contact-email"; With great power comes great responsibility. As an Arcade administrator, you have access to sensitive data, so Arcade Cloud requires strong authentication to keep your account safe. If Arcade refused your sign-in in the dashboard, the Arcade CLI, or an MCP client, this page shows you how to fix it. @@ -65,7 +62,7 @@ When you finish, Manage my account shows **Your account is protected**. Then sig - **Arcade dashboard:** sign in again. - **Arcade CLI:** run `arcade login` again. -- **MCP client:** sign in to Arcade in your system browser, then reconnect to the Arcade MCP URL. Each client does this differently, so check your client's guide under [MCP clients](/get-started/mcp-clients). +- **MCP client:** sign in to Arcade in your system browser, then reconnect to the Arcade MCP URL. Each client does this differently, so check your client's guide under [MCP clients](/en/get-started/mcp-clients). ## Having trouble? diff --git a/app/en/get-started/setup/windows-environment/page.mdx b/content/docs/get-started/setup/windows-environment.mdx similarity index 92% rename from app/en/get-started/setup/windows-environment/page.mdx rename to content/docs/get-started/setup/windows-environment.mdx index 620774346..c23766bc6 100644 --- a/app/en/get-started/setup/windows-environment/page.mdx +++ b/content/docs/get-started/setup/windows-environment.mdx @@ -1,12 +1,8 @@ --- -title: "Windows environment setup" -description: "Install and use Arcade CLI on Windows" +title: Windows environment setup +description: Install and use Arcade CLI on Windows --- -import { Steps, Callout, Tabs } from "nextra/components"; - -# Windows environment setup - Set up Arcade CLI on Windows using `uv` (recommended), with optional `pip` fallback guidance. ## Before you start @@ -289,6 +285,6 @@ are using WSL or Git Bash with Homebrew. ## Next steps -- [Get an API key](/get-started/setup/api-keys) -- [Run the MCP server quickstart](/get-started/quickstarts/mcp-server-quickstart) -- [Browse the Arcade CLI reference](/references/arcade-cli) +- [Get an API key](/en/get-started/setup/api-keys) +- [Run the MCP server quickstart](/en/get-started/quickstarts/mcp-server-quickstart) +- [Browse the Arcade CLI reference](/en/references/arcade-cli) diff --git a/content/docs/home.mdx b/content/docs/home.mdx new file mode 100644 index 000000000..e3dfe4744 --- /dev/null +++ b/content/docs/home.mdx @@ -0,0 +1,14 @@ +--- +title: Arcade Docs +sidebarTitle: Docs Home +description: Arcade is the enterprise-ready actions runtime for AI agents. Guides, resources, sample agents, and references for agent authorization, agent-optimized tools (MCP servers), and agent lifecycle governance. +icon: House +full: true +toc: false +breadcrumb: false +landing: true +--- + +import { LandingPage } from "@/components/pages/home/landing-page"; + +<LandingPage /> diff --git a/content/docs/learn/meta.json b/content/docs/learn/meta.json new file mode 100644 index 000000000..348785180 --- /dev/null +++ b/content/docs/learn/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Learn", + "icon": "Book" +} diff --git a/app/en/learn/server-level-vs-tool-level-auth/page.mdx b/content/docs/learn/server-level-vs-tool-level-auth.mdx similarity index 90% rename from app/en/learn/server-level-vs-tool-level-auth/page.mdx rename to content/docs/learn/server-level-vs-tool-level-auth.mdx index 730b6c7e7..acd12b1c6 100644 --- a/app/en/learn/server-level-vs-tool-level-auth/page.mdx +++ b/content/docs/learn/server-level-vs-tool-level-auth.mdx @@ -1,23 +1,19 @@ --- -title: "Server-Level vs Tool-Level Authorization" -description: "Understanding the difference between Resource Server auth and tool-level authorization in Arcade MCP servers" +title: Server-Level vs Tool-Level Authorization +description: Understanding the difference between Resource Server auth and tool-level authorization in Arcade MCP servers --- -import { Callout } from "nextra/components"; - -# Server-Level vs Tool-Level Authorization - Arcade MCP servers support two distinct layers of authorization that work together to provide comprehensive security. Understanding the difference is crucial for building secure, production-ready MCP servers. <Callout type="info"> **Using `arcade deploy`?** If you deploy your MCP server with [`arcade - deploy`](/build/arcade-deploy), Arcade handles + deploy`](/en/build/arcade-deploy), Arcade handles server-level security for you automatically. </Callout> ## Quick Comparison -| Aspect | [Resource Server Auth (Front-Door)](/build/create-tools/secure-your-server/secure-your-mcp-server) | [Tool-Level Authorization](/build/create-tools/tool-basics/create-tool-auth) | +| Aspect | [Resource Server Auth (Front-Door)](/en/build/create-tools/secure-your-server/secure-your-mcp-server) | [Tool-Level Authorization](/en/build/create-tools/tool-basics/create-tool-auth) | | --------------------- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | | **What it secures** | Access to your MCP server | Access to third-party APIs | | **Who authenticates** | The user calling your server | The user's access to external services | @@ -36,7 +32,7 @@ Resource Server auth ensures every request identifies the caller. It blocks unau ✅ **You need Resource Server auth if:** -- You've determined that [arcade deploy](/build/arcade-deploy) is not a good fit for your use case +- You've determined that [arcade deploy](/en/build/arcade-deploy) is not a good fit for your use case - You're running an HTTP MCP server in production - Your server has tools that require authorization or secrets - You need to identify which user is calling your server @@ -44,14 +40,14 @@ Resource Server auth ensures every request identifies the caller. It blocks unau ❌ **You don't need it if:** -- You're using [arcade deploy](/build/arcade-deploy) to secure your server +- You're using [arcade deploy](/en/build/arcade-deploy) to secure your server - You're using stdio transport - Your server only has public tools (no auth/secrets required) - You're doing local development only ### Example -```python filename="server.py" showLineNumbers +```python title="server.py" lineNumbers from arcade_mcp_server import MCPApp from arcade_mcp_server.resource_server import ResourceServerAuth, AuthorizationServerEntry @@ -97,7 +93,7 @@ Tool-level authorization lets your tools authenticate to external APIs using OAu ### Example -```python filename="server.py" showLineNumbers +```python title="server.py" lineNumbers from typing import Annotated from arcade_mcp_server import Context, MCPApp @@ -143,7 +139,7 @@ if __name__ == "__main__": The two authorization layers complement each other. Below is an example of a protected HTTP server with both server-level and tool-level authorization. -```python filename="server.py" {9-18, 20, 24, 33, 48} showLineNumbers +```python title="server.py" {9-18, 20, 24, 33, 48} lineNumbers from typing import Annotated import httpx @@ -208,7 +204,7 @@ if __name__ == "__main__": ### Q: Can I use tool-level auth without Resource Server auth? -**A:** Yes, but only for stdio transport or when using [arcade deploy](/build/arcade-deploy) (Arcade will protect your MCP server for you). +**A:** Yes, but only for stdio transport or when using [arcade deploy](/en/build/arcade-deploy) (Arcade will protect your MCP server for you). ### Q: Do I need Resource Server auth for local development? diff --git a/content/docs/meta.json b/content/docs/meta.json new file mode 100644 index 000000000..4a6a7e1d6 --- /dev/null +++ b/content/docs/meta.json @@ -0,0 +1,11 @@ +{ + "pages": [ + "home", + "get-started", + "build", + "operate", + "resources", + "learn", + "references" + ] +} diff --git a/app/en/operate/deploy/arcade-cloud/page.mdx b/content/docs/operate/deploy/arcade-cloud.mdx similarity index 89% rename from app/en/operate/deploy/arcade-cloud/page.mdx rename to content/docs/operate/deploy/arcade-cloud.mdx index c2481f55a..6cd81c4d0 100644 --- a/app/en/operate/deploy/arcade-cloud/page.mdx +++ b/content/docs/operate/deploy/arcade-cloud.mdx @@ -1,16 +1,13 @@ --- -title: "Arcade Cloud infrastructure" -description: "Learn about the infrastructure, data storage, and data sovereignty options for Arcade Cloud" +title: Arcade Cloud infrastructure +sidebarTitle: Arcade Cloud +description: Learn about the infrastructure, data storage, and data sovereignty options for Arcade Cloud --- -import { Callout } from "nextra/components"; - -# Arcade Cloud infrastructure - Arcade Cloud is Arcade's fully-managed SaaS platform: Arcade handles hosting, scaling, and operations so you can focus on building. This page covers the infrastructure behind Arcade Cloud, including networking, data storage, data protection, and sovereignty options. <Callout type="info"> -This page applies to **Arcade Cloud** only. If you self-host Arcade, you control your own infrastructure and data residency. See [on-premises deployment](/operate/deploy/helm) for details. +This page applies to **Arcade Cloud** only. If you self-host Arcade, you control your own infrastructure and data residency. See [on-premises deployment](/en/operate/deploy/helm) for details. </Callout> ## Sovereignty @@ -35,7 +32,7 @@ Traffic from Arcade Cloud exits from the following IP addresses: ## VPC peering -VPC peering is available for enterprise customers upon request. If you want VPC peering, [contact Arcade](/resources/contact-us). +VPC peering is available for enterprise customers upon request. If you want VPC peering, [contact Arcade](/en/resources/contact-us). ## Data storage @@ -122,8 +119,8 @@ Training data is retained for up to 5 years, which is sufficient for model devel ### Regulated Customers -If your organization has strict data residency requirements—for example, in financial services, healthcare, or government—or you operate within a legal regime that requires data storage in a specific country, you can deploy Arcade on-premises or in your preferred cloud region using a [self-hosted deployment](/operate/deploy/helm). This keeps all sensitive data within your own infrastructure. +If your organization has strict data residency requirements—for example, in financial services, healthcare, or government—or you operate within a legal regime that requires data storage in a specific country, you can deploy Arcade on-premises or in your preferred cloud region using a [self-hosted deployment](/en/operate/deploy/helm). This keeps all sensitive data within your own infrastructure. ## Questions -For compliance inquiries or data protection questions, [contact us](/resources/contact-us). +For compliance inquiries or data protection questions, [contact us](/en/resources/contact-us). diff --git a/app/en/operate/deploy/architecture/page.mdx b/content/docs/operate/deploy/architecture.mdx similarity index 84% rename from app/en/operate/deploy/architecture/page.mdx rename to content/docs/operate/deploy/architecture.mdx index 929216c5c..aee3ec9d6 100644 --- a/app/en/operate/deploy/architecture/page.mdx +++ b/content/docs/operate/deploy/architecture.mdx @@ -1,16 +1,13 @@ --- -title: "Platform architecture" -description: "The services that make up a self-hosted Arcade deployment and how they connect" +title: Platform architecture +description: The services that make up a self-hosted Arcade deployment and how they connect --- -import { Callout } from "nextra/components"; import Image from "next/image"; -# Platform architecture +A self-hosted Arcade deployment runs the services below on your Kubernetes cluster. The [Helm chart](/en/operate/deploy/helm) and the [Azure](/en/operate/deploy/azure) and [AWS](/en/operate/deploy/aws) marketplace deployments install the same platform. -A self-hosted Arcade deployment runs the services below on your Kubernetes cluster. The [Helm chart](/operate/deploy/helm) and the [Azure](/operate/deploy/azure) and [AWS](/operate/deploy/aws) marketplace deployments install the same platform. - -This page describes what each service does and what it connects to. For installation steps, see [Self-host with Helm](/operate/deploy/helm). +This page describes what each service does and what it connects to. For installation steps, see [Self-host with Helm](/en/operate/deploy/helm). ## Reference topology @@ -72,11 +69,11 @@ The Engine is the API that agents call. It: - Serves the tool catalog, including each tool's schema - Executes tool calls, dispatching each one to the MCP server that owns the tool and returning the result - Runs the token vault: it takes each end user through the OAuth flow for a provider such as Google or Slack, stores the resulting token encrypted, refreshes it before it expires, and supplies it to the tool at execution time -- Serves [MCP gateways](/operate/governance/mcp-gateways), the scoped MCP endpoints that agents and MCP clients connect to +- Serves [MCP gateways](/en/operate/governance/mcp-gateways), the scoped MCP endpoints that agents and MCP clients connect to - Registers MCP servers and reconciles their tool catalogs into its database -- Stores [auth provider](/references/auth-providers) configuration and per-project tool secrets -- Records each tool execution, which the dashboard's execution history and [audit logs](/operate/governance/audit-logs) read from -- Calls [contextual access](/operate/governance/contextual-access) hooks before and after a tool runs, which can allow, deny, or rate-limit the call +- Stores [auth provider](/en/references/auth-providers) configuration and per-project tool secrets +- Records each tool execution, which the dashboard's execution history and [audit logs](/en/operate/governance/audit-logs) read from +- Calls [contextual access](/en/operate/governance/contextual-access) hooks before and after a tool runs, which can allow, deny, or rate-limit the call The Engine calls the Coordinator to validate API keys and resolve the account, organization, and project behind a request. It calls MCP servers over HTTP to execute tools. @@ -85,7 +82,7 @@ The Engine calls the Coordinator to validate API keys and resolve the account, o The Coordinator owns identity and tenancy records: - Accounts, organizations, projects, project API keys, and member invitations -- The connection to your OIDC identity provider, and the [User Sources](/operate/identity/user-sources) that authenticate the end users of an MCP gateway +- The connection to your OIDC identity provider, and the [User Sources](/en/operate/identity/user-sources) that authenticate the end users of an MCP gateway - Role assignments, which it evaluates against OpenFGA when you turn on role-based access control It handles two flows that require a reachable endpoint: @@ -116,14 +113,14 @@ It runs in the browser and calls only the Experience API. An MCP server hosts tool implementations and runs them on request. A deployment can draw tools from three sources: -- **The bundled MCP server.** Each deployment includes one, preloaded with Arcade's integration toolkits for services such as Gmail, Slack, Jira, GitHub, and Notion. See [Arcade Worker](/operate/deploy/worker) for how it runs toolkits and the settings you control. +- **The bundled MCP server.** Each deployment includes one, preloaded with Arcade's integration toolkits for services such as Gmail, Slack, Jira, GitHub, and Notion. See [Arcade Worker](/en/operate/deploy/worker) for how it runs toolkits and the settings you control. - **MCP servers you write and deploy.** Each one is a separate deployment, so you can scale or isolate it independently. -- **[Remote MCP servers](/operate/governance/remote-mcp-servers).** Register an MCP server that Arcade does not host, whether your team runs it or a vendor does, and its tools join the same catalog. This adds nothing to your cluster. +- **[Remote MCP servers](/en/operate/governance/remote-mcp-servers).** Register an MCP server that Arcade does not host, whether your team runs it or a vendor does, and its tools join the same catalog. This adds nothing to your cluster. In each case the Engine sends the tool call over HTTP with the end-user credential from the token vault, and the MCP server returns the result. The tool catalog, gateways, auth, and audit trail work the same way regardless of where the server runs. <Callout type="info"> -MCP servers appear as **workers** in the Helm values and in the Engine's API. The two terms refer to the same thing. Neither is an [MCP gateway](/operate/governance/mcp-gateways), which is a feature of the Engine rather than a deployed service. +MCP servers appear as **workers** in the Helm values and in the Engine's API. The two terms refer to the same thing. Neither is an [MCP gateway](/en/operate/governance/mcp-gateways), which is a feature of the Engine rather than a deployed service. </Callout> ### OpenFGA @@ -149,7 +146,7 @@ The platform initiates the remaining connections outbound: | Engine | Coordinator | Validating API keys and resolving accounts, organizations, and projects | | Engine | MCP servers | Executing tool calls, whether the server runs in your cluster or elsewhere | | Engine | OAuth provider token endpoints | Getting and refreshing end-user tokens. Restrict with `engine.ssrfAllowlist`. | -| Engine | Your [contextual access](/operate/governance/contextual-access) hooks | Policy checks before and after each tool call | +| Engine | Your [contextual access](/en/operate/governance/contextual-access) hooks | Policy checks before and after each tool call | | Coordinator | Your OIDC identity provider | Signing operators in | | MCP servers | Third-party APIs | The work each tool does | | Engine, Coordinator, MCP servers | Arcade's usage endpoint | Usage information, if you turn it on | @@ -180,6 +177,6 @@ The Helm chart can deploy PostgreSQL and Redis alongside the platform for evalua ## Next steps -- [Self-host with Helm](/operate/deploy/helm) to install the platform -- [Create an MCP gateway](/operate/governance/mcp-gateways) to scope tools and auth for each client -- [Set up a User Source](/operate/identity/user-sources) to authenticate your end users +- [Self-host with Helm](/en/operate/deploy/helm) to install the platform +- [Create an MCP gateway](/en/operate/governance/mcp-gateways) to scope tools and auth for each client +- [Set up a User Source](/en/operate/identity/user-sources) to authenticate your end users diff --git a/app/en/operate/deploy/aws/page.mdx b/content/docs/operate/deploy/aws.mdx similarity index 74% rename from app/en/operate/deploy/aws/page.mdx rename to content/docs/operate/deploy/aws.mdx index b432e36c9..ae65ca272 100644 --- a/app/en/operate/deploy/aws/page.mdx +++ b/content/docs/operate/deploy/aws.mdx @@ -1,21 +1,19 @@ --- -title: "Deploy on AWS" -description: "Deploy Arcade into your own AWS account through a private offer" +title: Deploy Arcade on AWS +metaTitle: Deploy on AWS +sidebarTitle: AWS Marketplace +description: Deploy Arcade into your own AWS account through a private offer --- -import { Callout, Steps } from "nextra/components"; - -# Deploy Arcade on AWS - Arcade runs on AWS as a full platform deployment into **your own AWS account**. The AWS offering is currently available through a **private offer** rather than a public self-serve listing. <Callout type="info"> -AWS is available through a private offer. View the [Arcade listing on AWS Marketplace](https://aws.amazon.com/marketplace/seller-profile?id=seller-qbstsdnxkltx6) and [contact us](https://www.arcade.dev/contact) to receive a private offer for your account. If you'd rather manage the platform yourself, see [Self-host with Helm](/operate/deploy/helm). +AWS is available through a private offer. View the [Arcade listing on AWS Marketplace](https://aws.amazon.com/marketplace/seller-profile?id=seller-qbstsdnxkltx6) and [contact us](https://www.arcade.dev/contact) to receive a private offer for your account. If you'd rather manage the platform yourself, see [Self-host with Helm](/en/operate/deploy/helm). </Callout> ## What gets deployed -The AWS deployment stands up the complete Arcade platform in your account. See [Platform architecture](/operate/deploy/architecture) for what each Arcade service does. It runs on managed AWS services: +The AWS deployment stands up the complete Arcade platform in your account. See [Platform architecture](/en/operate/deploy/architecture) for what each Arcade service does. It runs on managed AWS services: | AWS service | Role | | --- | --- | @@ -29,13 +27,13 @@ The AWS deployment stands up the complete Arcade platform in your account. See [ - **AWS account** with permission to accept a Marketplace private offer and deploy the stack. - **Region.** Confirm the offer targets your preferred region. - **DNS.** Arcade assigns a managed hostname at deploy time, in the form `<id>.aws.myarcade.dev`. -- **Identity provider.** Have an [OIDC identity provider](/references/auth-providers) ready — see below. +- **Identity provider.** Have an [OIDC identity provider](/en/references/auth-providers) ready — see below. ## Set up your identity provider Arcade signs users in through your OpenID Connect (OIDC) identity provider. The provider authenticates dashboard users and backs the tokens that MCP gateways validate, so set it up before you deploy. -1. Register an application with your identity provider. Arcade works with [Microsoft Entra ID, Okta, Auth0, or Keycloak](/references/auth-providers), or any standards-compliant OIDC provider. +1. Register an application with your identity provider. Arcade works with [Microsoft Entra ID, Okta, Auth0, or Keycloak](/en/references/auth-providers), or any standards-compliant OIDC provider. 2. Copy the application's client ID, generate a client secret, and note the issuer URL. For Microsoft Entra ID, use the v2.0 issuer `https://login.microsoftonline.com/<tenant-id>/v2.0`. 3. Provide the client ID, client secret, and issuer in the deployment parameters. 4. After you deploy, register the redirect URIs shown in the deployment outputs on the application, then sign in to the dashboard. @@ -64,6 +62,6 @@ Open the dashboard URL from the deployment output and sign in with your identity ## Next steps -- [Create an MCP Gateway](/operate/governance/mcp-gateways) to scope tools and auth for each client -- [Connect an MCP client](/get-started/mcp-clients) to a gateway URL -- [Set up a User Source](/operate/identity/user-sources) to authenticate end users with your own identity provider +- [Create an MCP Gateway](/en/operate/governance/mcp-gateways) to scope tools and auth for each client +- [Connect an MCP client](/en/get-started/mcp-clients) to a gateway URL +- [Set up a User Source](/en/operate/identity/user-sources) to authenticate end users with your own identity provider diff --git a/app/en/operate/deploy/azure/page.mdx b/content/docs/operate/deploy/azure.mdx similarity index 81% rename from app/en/operate/deploy/azure/page.mdx rename to content/docs/operate/deploy/azure.mdx index b3ebcbacd..7803cfc8f 100644 --- a/app/en/operate/deploy/azure/page.mdx +++ b/content/docs/operate/deploy/azure.mdx @@ -1,16 +1,14 @@ --- -title: "Deploy on Azure" -description: "Deploy Arcade into your own Azure subscription from the Azure Marketplace" +title: Deploy Arcade on Azure +metaTitle: Deploy on Azure +sidebarTitle: Azure Marketplace +description: Deploy Arcade into your own Azure subscription from the Azure Marketplace --- -import { Callout, Steps } from "nextra/components"; - -# Deploy Arcade on Azure - Arcade is available on the Azure Marketplace as a managed application. You deploy it into **your own Azure subscription**, where it's managed by Arcade: your data and the services that run Arcade stay in your subscription, while Arcade handles upgrades and day-to-day operations. <Callout type="info"> -The Azure Marketplace deployment is a **full platform deployment** — it stands up the complete Arcade platform in your cloud. If you'd rather manage the platform yourself on an existing Kubernetes cluster, see [Self-host with Helm](/operate/deploy/helm). +The Azure Marketplace deployment is a **full platform deployment** — it stands up the complete Arcade platform in your cloud. If you'd rather manage the platform yourself on an existing Kubernetes cluster, see [Self-host with Helm](/en/operate/deploy/helm). </Callout> <GuideOverview> @@ -24,7 +22,7 @@ A production Arcade platform running in your own Azure subscription, deployed fr - An Azure subscription with the **Owner** role - A Microsoft Entra tenant (personal Microsoft accounts aren't supported) -- An [OIDC identity provider](/references/auth-providers) +- An [OIDC identity provider](/en/references/auth-providers) </GuideOverview.Prerequisites> @@ -43,7 +41,7 @@ The managed application provisions everything Arcade needs inside a managed reso | Azure service | Role | | --- | --- | -| **Azure Kubernetes Service (AKS)** | Private cluster that runs the [Arcade services](/operate/deploy/architecture) | +| **Azure Kubernetes Service (AKS)** | Private cluster that runs the [Arcade services](/en/operate/deploy/architecture) | | **Azure Key Vault** | Stores database, cache, identity-provider, and encryption secrets | | **Azure Database for PostgreSQL Flexible Server** | Primary datastore | | **Azure Managed Redis** | Cache and streams | @@ -54,15 +52,15 @@ The managed application provisions everything Arcade needs inside a managed reso - **Subscription and role.** Deploy into a subscription where you hold the **Owner** role, with the spending limit turned off. - **Microsoft Entra tenant.** The deployment federates sign-in to your Entra tenant. Personal Microsoft accounts aren't supported. -- **Region and quota.** Confirm the offer is available in your target region and that the subscription has enough vCPU quota to run an AKS cluster. If you're unsure, [contact us](/resources/contact-us). +- **Region and quota.** Confirm the offer is available in your target region and that the subscription has enough vCPU quota to run an AKS cluster. If you're unsure, [contact us](/en/resources/contact-us). - **DNS.** Arcade assigns a managed hostname at deploy time, in the form `<id>.az.myarcade.dev`. -- **Identity provider.** Have an [OIDC identity provider](/references/auth-providers) ready — see the next section. +- **Identity provider.** Have an [OIDC identity provider](/en/references/auth-providers) ready — see the next section. ## Set up your identity provider Arcade signs users in through your OpenID Connect (OIDC) identity provider. The provider authenticates dashboard users and backs the tokens that MCP gateways validate, so set it up before you deploy. -1. Register an application with your identity provider. Arcade works with [Microsoft Entra ID, Okta, Auth0, or Keycloak](/references/auth-providers), or any standards-compliant OIDC provider. +1. Register an application with your identity provider. Arcade works with [Microsoft Entra ID, Okta, Auth0, or Keycloak](/en/references/auth-providers), or any standards-compliant OIDC provider. 2. Copy the application's client ID, generate a client secret, and note the issuer URL. For Microsoft Entra ID, use the v2.0 issuer `https://login.microsoftonline.com/<tenant-id>/v2.0`. 3. Provide the client ID, client secret, and issuer in the create form. 4. After you deploy, register the redirect URIs shown in the deployment outputs on the application, then sign in to the dashboard. @@ -99,6 +97,6 @@ Open the dashboard URL from the deployment output and sign in with your identity ## Next steps -- [Create an MCP Gateway](/operate/governance/mcp-gateways) to scope tools and auth for each client -- [Connect an MCP client](/get-started/mcp-clients) to a gateway URL -- [Set up a User Source](/operate/identity/user-sources) to authenticate end users with your own identity provider +- [Create an MCP Gateway](/en/operate/governance/mcp-gateways) to scope tools and auth for each client +- [Connect an MCP client](/en/get-started/mcp-clients) to a gateway URL +- [Set up a User Source](/en/operate/identity/user-sources) to authenticate end users with your own identity provider diff --git a/app/en/operate/deploy/gcp/page.mdx b/content/docs/operate/deploy/gcp.mdx similarity index 63% rename from app/en/operate/deploy/gcp/page.mdx rename to content/docs/operate/deploy/gcp.mdx index 59454fe64..f1aee480a 100644 --- a/app/en/operate/deploy/gcp/page.mdx +++ b/content/docs/operate/deploy/gcp.mdx @@ -1,17 +1,15 @@ --- -title: "Deploy on GCP" -description: "The Arcade deployment for Google Cloud is coming soon" +title: Deploy Arcade on GCP +metaTitle: Deploy on GCP +sidebarTitle: GCP (coming soon) +description: The Arcade deployment for Google Cloud is coming soon --- -import { Callout } from "nextra/components"; - -# Deploy Arcade on GCP - <Callout type="info"> A turnkey Arcade deployment for Google Cloud is **in development**. [Request early access](https://www.arcade.dev/contact) to be notified when it's available and to help shape it. </Callout> -Arcade on GCP will deploy the [full platform](/operate/deploy/architecture) into your own Google Cloud project, using managed Google Cloud services. +Arcade on GCP will deploy the [full platform](/en/operate/deploy/architecture) into your own Google Cloud project, using managed Google Cloud services. ## Planned architecture @@ -27,9 +25,9 @@ Arcade on GCP will deploy the [full platform](/operate/deploy/architecture) into ## Deploy on GCP today -You don't have to wait to run Arcade on Google Cloud. You can [self-host with Helm](/operate/deploy/helm) on an existing GKE cluster, bringing your own Cloud SQL and Memorystore instances. +You don't have to wait to run Arcade on Google Cloud. You can [self-host with Helm](/en/operate/deploy/helm) on an existing GKE cluster, bringing your own Cloud SQL and Memorystore instances. ## Next steps -- [Self-host with Helm](/operate/deploy/helm) on GKE +- [Self-host with Helm](/en/operate/deploy/helm) on GKE - [Request early access](https://www.arcade.dev/contact) to the GCP deployment diff --git a/app/en/operate/deploy/helm-arcade-deploy/page.mdx b/content/docs/operate/deploy/helm-arcade-deploy.mdx similarity index 94% rename from app/en/operate/deploy/helm-arcade-deploy/page.mdx rename to content/docs/operate/deploy/helm-arcade-deploy.mdx index 72770560a..3694b3e00 100644 --- a/app/en/operate/deploy/helm-arcade-deploy/page.mdx +++ b/content/docs/operate/deploy/helm-arcade-deploy.mdx @@ -1,13 +1,9 @@ --- -title: "Arcade Deploy on your own cluster (Helm)" -description: "Turn on Arcade Deploy in a self-hosted Arcade installation so developers can run arcade deploy against your own Kubernetes cluster" +title: Arcade Deploy on your own cluster (Helm) +description: Turn on Arcade Deploy in a self-hosted Arcade installation so developers can run arcade deploy against your own Kubernetes cluster --- -import { Callout, Steps } from "nextra/components"; - -# Arcade Deploy on your own cluster (Helm) - -This page is for platform operators who run Arcade with the [Helm chart](/operate/deploy/helm) and want developers to ship custom MCP servers into that installation with `arcade deploy`. Once you turn deployments on, the Arcade Engine runs each deployed server as a workload in your own cluster. Bundles go to an object store you own, and runner images come from a registry you choose. +This page is for platform operators who run Arcade with the [Helm chart](/en/operate/deploy/helm) and want developers to ship custom MCP servers into that installation with `arcade deploy`. Once you turn deployments on, the Arcade Engine runs each deployed server as a workload in your own cluster. Bundles go to an object store you own, and runner images come from a registry you choose. Bundles stay in your environment: the chart requires an artifact store (`deployments.artifacts.url`), and the Arcade Engine publishes bundles only there. Usage-event tracking (`usageTracking.enabled`) is off by default in the chart. Leave it off so your installation sends no usage events to Arcade. @@ -23,7 +19,7 @@ The Arcade Engine's service account gets a namespaced Role in the workers namesp ## Prerequisites -- A working Arcade installation from the [Helm chart](/operate/deploy/helm), with `gateway.hostname`, `ingress.hostname`, or `deployments.domain` set +- A working Arcade installation from the [Helm chart](/en/operate/deploy/helm), with `gateway.hostname`, `ingress.hostname`, or `deployments.domain` set - A bucket in an S3-compatible store, Google Cloud Storage, or Azure Blob Storage for release bundles - Credentials for that bucket that the Arcade Engine can pick up from its environment. The Arcade Engine reads them from the ambient cloud credential chain, never from the bucket URL and never from a deploy request. See [Bucket credentials](#bucket-credentials). - Access from your cluster to the runner image, either `arcadedev/runner` on Docker Hub or a copy in your own registry @@ -71,7 +67,7 @@ kubectl -n arcade create secret generic arcade-bundle-store \ --from-literal=AWS_SECRET_ACCESS_KEY=... ``` -```yaml filename="values.yaml" +```yaml title="values.yaml" engine: extraEnvFrom: - secretRef: @@ -98,7 +94,7 @@ The identity applies to the Arcade Engine's Deployment only. The Arcade Engine's EKS IRSA: -```yaml filename="values.yaml" +```yaml title="values.yaml" engine: serviceAccount: annotations: @@ -107,7 +103,7 @@ engine: Azure Workload Identity. Also name the storage account, with `AZURE_STORAGE_ACCOUNT` in `engine.extraEnv` or `storage_account=` in `deployments.artifacts.url`: -```yaml filename="values.yaml" +```yaml title="values.yaml" engine: serviceAccount: annotations: @@ -118,7 +114,7 @@ engine: GKE Workload Identity: -```yaml filename="values.yaml" +```yaml title="values.yaml" engine: serviceAccount: annotations: @@ -127,7 +123,7 @@ engine: GCP service account key file, mounted from a Secret named `gcp-key` with a `key.json` entry: -```yaml filename="values.yaml" +```yaml title="values.yaml" engine: extraVolumes: - name: gcp-key @@ -209,7 +205,7 @@ For S3, this policy covers both operations, scoped to one bucket. Replace `arcad Deployments are off by default. Turn them on and configure your artifact store. The Arcade Engine also needs credentials for the bucket. See [Bucket credentials](#bucket-credentials) and [Minimal working configuration](#minimal-working-configuration): -```yaml filename="values.yaml" +```yaml title="values.yaml" deployments: enabled: true artifacts: @@ -248,9 +244,9 @@ Then ask them to [deploy a server](#deploy-a-server) and check that it reaches * ## Minimal working configuration -This is the only addition a working Arcade install needs to turn deployments on. The rest of the install comes from the [Helm chart guide](/operate/deploy/helm). The hostname must already be set through `gateway.hostname`, `ingress.hostname`, or `deployments.domain`, and the Arcade Engine needs credentials for the bucket as described in [Bucket credentials](#bucket-credentials). +This is the only addition a working Arcade install needs to turn deployments on. The rest of the install comes from the [Helm chart guide](/en/operate/deploy/helm). The hostname must already be set through `gateway.hostname`, `ingress.hostname`, or `deployments.domain`, and the Arcade Engine needs credentials for the bucket as described in [Bucket credentials](#bucket-credentials). -```yaml filename="values.yaml" +```yaml title="values.yaml" deployments: enabled: true artifacts: @@ -267,7 +263,7 @@ Each option below combines the deployments values with credentials, and you merg Option 1, static keys. Use an existing Secret named `arcade-bundle-store` that holds `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. See [Static keys](#static-keys) to create it. No keys go in `values.yaml`: -```yaml filename="values.yaml" +```yaml title="values.yaml" deployments: enabled: true artifacts: @@ -280,7 +276,7 @@ engine: Option 2, EKS IRSA. Annotate the Arcade Engine's ServiceAccount with your IAM role. IRSA needs chart 1.11.2 or later: -```yaml filename="values.yaml" +```yaml title="values.yaml" deployments: enabled: true artifacts: @@ -335,7 +331,7 @@ Arcade will fail to pull the server bundle if the signed URL is invalid. With Az For a private mirror, push `arcadedev/runner` to your registry and point the chart at it: -```yaml filename="values.yaml" +```yaml title="values.yaml" deployments: registry: server: registry.example.internal @@ -401,7 +397,7 @@ arcade login --url https://arcade.example.com arcade deploy -e src/my_server/server.py ``` -See [Arcade Deploy](/build/arcade-deploy) for signing in, picking a project and deploying, and the [CLI cheat sheet](/references/cli-cheat-sheet) for every flag. +See [Arcade Deploy](/en/build/arcade-deploy) for signing in, picking a project and deploying, and the [CLI cheat sheet](/en/references/cli-cheat-sheet) for every flag. - Deploy MCP servers built with the Arcade MCP framework for Python. The Arcade Engine refuses a bundle without a Python project definition or with a missing entrypoint. - The Arcade Engine refuses a changed redeploy while the previous one is still rolling out. @@ -446,7 +442,7 @@ kubectl -n arcade-workers get resourcequota ## Upgrades -Upgrade the chart with `helm upgrade` as described in [Self-host with Helm](/operate/deploy/helm#upgrade-and-roll-back). Keep these points in mind: +Upgrade the chart with `helm upgrade` as described in [Self-host with Helm](/en/operate/deploy/helm#upgrade-and-roll-back). Keep these points in mind: - **Runner image.** With `deployments.image.tag` empty, the runner tag follows the chart's `appVersion`. Pin `deployments.image.tag` to control which runner tag the Arcade Engine configures. - **Private registry.** Mirror the new runner tag into your registry before you upgrade. @@ -476,6 +472,6 @@ When an upgrade changes the runner image tag, the reconciler moves running deplo ## Next steps -- [Deploy an MCP server with Arcade Deploy](/build/arcade-deploy) -- [Create an MCP Gateway](/operate/governance/mcp-gateways) to give clients access to deployed servers' tools -- [Platform architecture](/operate/deploy/architecture) +- [Deploy an MCP server with Arcade Deploy](/en/build/arcade-deploy) +- [Create an MCP Gateway](/en/operate/governance/mcp-gateways) to give clients access to deployed servers' tools +- [Platform architecture](/en/operate/deploy/architecture) diff --git a/app/en/operate/deploy/helm/container-registry/page.mdx b/content/docs/operate/deploy/helm/container-registry.mdx similarity index 93% rename from app/en/operate/deploy/helm/container-registry/page.mdx rename to content/docs/operate/deploy/helm/container-registry.mdx index 6e353da6d..4dfa9bb7c 100644 --- a/app/en/operate/deploy/helm/container-registry/page.mdx +++ b/content/docs/operate/deploy/helm/container-registry.mdx @@ -1,13 +1,9 @@ --- -title: "Use your own container registry" -description: "Pull the Helm-installed Arcade platform's images from your own registry mirror, including for air-gapped installs" +title: Use your own container registry +description: Pull the Helm-installed Arcade platform's images from your own registry mirror, including for air-gapped installs --- -import { Callout } from "nextra/components"; - -# Use your own container registry - -This guide covers pulling every image the [Helm-installed Arcade platform](/operate/deploy/helm) needs from a registry you run, instead of Docker Hub. +This guide covers pulling every image the [Helm-installed Arcade platform](/en/operate/deploy/helm) needs from a registry you run, instead of Docker Hub. ## When you need it @@ -84,7 +80,7 @@ The init containers use `supportImages.busybox` (`generate-env-file`), `supportI ### The Arcade Deploy runner image -If you enable [Arcade Deploy](/build/arcade-deploy), the Arcade Engine starts every deployed server from the runner image in the workers namespace: +If you enable [Arcade Deploy](/en/build/arcade-deploy), the Arcade Engine starts every deployed server from the runner image in the workers namespace: - The image is `deployments.registry.server` joined with `deployments.image.repository` (default `arcadedev/runner`). With `deployments.registry.server` empty, the repository is used as-is. Its tag is `deployments.image.tag`, or the chart's app version when that's empty. - Deployed server pods use the Secrets listed in `deployments.registry.imagePullSecrets`. These Secrets must exist in the workers namespace (`deployments.namespace`), not the release namespace. diff --git a/app/en/operate/deploy/helm/page.mdx b/content/docs/operate/deploy/helm/index.mdx similarity index 64% rename from app/en/operate/deploy/helm/page.mdx rename to content/docs/operate/deploy/helm/index.mdx index d89a7597e..8bb4f50a9 100644 --- a/app/en/operate/deploy/helm/page.mdx +++ b/content/docs/operate/deploy/helm/index.mdx @@ -1,35 +1,33 @@ --- -title: "Self-host with Helm" -description: "Deploy and run the Arcade platform yourself on your own Kubernetes cluster" +title: Self-host Arcade with Helm +metaTitle: Self-host with Helm +sidebarTitle: Self-host with Helm +description: Deploy and run the Arcade platform yourself on your own Kubernetes cluster --- -import { Callout, Steps } from "nextra/components"; - -# Self-host Arcade with Helm - The Arcade Helm chart deploys the full Arcade platform onto a Kubernetes cluster you manage. It's the bring-your-own-Kubernetes alternative to the cloud marketplaces: you control the cluster, the data, and the upgrade cadence. <Callout type="info"> -If you'd rather not manage Kubernetes yourself, the [Azure](/operate/deploy/azure) and [AWS](/operate/deploy/aws) marketplace deployments run the same platform as turnkey, Arcade-operated deployments in your own cloud account. +If you'd rather not manage Kubernetes yourself, the [Azure](/en/operate/deploy/azure) and [AWS](/en/operate/deploy/aws) marketplace deployments run the same platform as turnkey, Arcade-operated deployments in your own cloud account. </Callout> ## What it deploys -The chart installs the complete platform into a namespace on your cluster, along with the supporting controllers it needs. See [Platform architecture](/operate/deploy/architecture) for what each service is responsible for and how they connect. +The chart installs the complete platform into a namespace on your cluster, along with the supporting controllers it needs. See [Platform architecture](/en/operate/deploy/architecture) for what each service is responsible for and how they connect. ## Prerequisites - **Kubernetes** 1.30 or later - **Helm** 3.8 or later (for OCI chart support) - **PostgreSQL** and **Redis** — bundled by the chart for evaluation, or bring your own managed instances for production -- An [OIDC identity provider](/references/auth-providers) +- An [OIDC identity provider](/en/references/auth-providers) - An ingress controller or Gateway API implementation, TLS certificates, and a DNS hostname ## Set up your identity provider Arcade signs users in through your OpenID Connect (OIDC) identity provider. The provider authenticates dashboard users and backs the tokens that MCP gateways validate, so set it up before you deploy. -1. Register an application with your identity provider. Arcade works with [Microsoft Entra ID, Okta, Auth0, or Keycloak](/references/auth-providers), or any standards-compliant OIDC provider. +1. Register an application with your identity provider. Arcade works with [Microsoft Entra ID, Okta, Auth0, or Keycloak](/en/references/auth-providers), or any standards-compliant OIDC provider. 2. Copy the application's client ID, generate a client secret, and note the issuer URL. For Microsoft Entra ID, use the v2.0 issuer `https://login.microsoftonline.com/<tenant-id>/v2.0`. 3. Provide the client ID, client secret, and issuer in your Helm values. 4. After you deploy, register the redirect URI for your hostname's `/signin/oidc/callback` path on the application, then sign in to the dashboard. @@ -59,16 +57,16 @@ Once the pods are running, open your configured hostname and sign in through you Configure the platform through Helm values. The chart covers: -- **Components** — the [platform services](/operate/deploy/architecture) and how many replicas of each to run -- **Workers** — which toolkits each [Arcade Worker](/operate/deploy/worker) serves and keeps warm, and how many runtimes it runs at once -- **Identity** — your OIDC identity provider and [OAuth providers](/references/auth-providers) +- **Components** — the [platform services](/en/operate/deploy/architecture) and how many replicas of each to run +- **Workers** — which toolkits each [Arcade Worker](/en/operate/deploy/worker) serves and keeps warm, and how many runtimes it runs at once +- **Identity** — your OIDC identity provider and [OAuth providers](/en/references/auth-providers) - **Data stores** — bundled or external PostgreSQL and Redis - **Networking** — ingress or Gateway API, TLS, and the `engine.ssrfAllowlist` for reaching internal services - **Security** — encryption keys, network policies, and RBAC - **Telemetry** — OpenTelemetry (OTLP) logs, metrics, and traces <Callout type="info"> -The [Helm values reference](/operate/deploy/helm/values-reference) lists every value and default, generated from each chart release. Use it as the source of truth. +The [Helm values reference](/en/operate/deploy/helm/values-reference) lists every value and default, generated from each chart release. Use it as the source of truth. </Callout> ## Upgrade and roll back @@ -77,6 +75,6 @@ Upgrade to a new chart version with `helm upgrade`, and roll back with `helm rol ## Next steps -- [Turn on Arcade Deploy](/operate/deploy/helm-arcade-deploy) so developers can deploy custom MCP servers into your cluster -- [Create an MCP Gateway](/operate/governance/mcp-gateways) to scope tools and auth for each client -- [Set up a User Source](/operate/identity/user-sources) to authenticate end users with your own identity provider +- [Turn on Arcade Deploy](/en/operate/deploy/helm-arcade-deploy) so developers can deploy custom MCP servers into your cluster +- [Create an MCP Gateway](/en/operate/governance/mcp-gateways) to scope tools and auth for each client +- [Set up a User Source](/en/operate/identity/user-sources) to authenticate end users with your own identity provider diff --git a/app/en/operate/deploy/helm/internal-networking/page.mdx b/content/docs/operate/deploy/helm/internal-networking.mdx similarity index 93% rename from app/en/operate/deploy/helm/internal-networking/page.mdx rename to content/docs/operate/deploy/helm/internal-networking.mdx index d95dc5a85..ac57d18fb 100644 --- a/app/en/operate/deploy/helm/internal-networking/page.mdx +++ b/content/docs/operate/deploy/helm/internal-networking.mdx @@ -1,13 +1,9 @@ --- -title: "Internal networking" -description: "Run a Helm-installed Arcade platform with no public ingress, behind an internal load balancer or Gateway API, and let it reach MCP servers on your internal network" +title: Internal networking +description: Run a Helm-installed Arcade platform with no public ingress, behind an internal load balancer or Gateway API, and let it reach MCP servers on your internal network --- -import { Callout } from "nextra/components"; - -# Internal networking - -This guide covers running the [Helm-installed Arcade platform](/operate/deploy/helm) entirely inside your network: no public ingress, traffic through an internal load balancer or a Gateway API implementation you run, and MCP servers on internal addresses that the Arcade Engine is allowed to reach. +This guide covers running the [Helm-installed Arcade platform](/en/operate/deploy/helm) entirely inside your network: no public ingress, traffic through an internal load balancer or a Gateway API implementation you run, and MCP servers on internal addresses that the Arcade Engine is allowed to reach. ## When you need it diff --git a/app/en/operate/deploy/helm/ipv6-dual-stack/page.mdx b/content/docs/operate/deploy/helm/ipv6-dual-stack.mdx similarity index 93% rename from app/en/operate/deploy/helm/ipv6-dual-stack/page.mdx rename to content/docs/operate/deploy/helm/ipv6-dual-stack.mdx index 16ea2448b..7b6a1e51a 100644 --- a/app/en/operate/deploy/helm/ipv6-dual-stack/page.mdx +++ b/content/docs/operate/deploy/helm/ipv6-dual-stack.mdx @@ -1,13 +1,9 @@ --- -title: "IPv6 and dual-stack" -description: "Run the Helm-installed Arcade platform on a dual-stack Kubernetes cluster, and what doesn't support IPv6 yet" +title: IPv6 and dual-stack +description: Run the Helm-installed Arcade platform on a dual-stack Kubernetes cluster, and what doesn't support IPv6 yet --- -import { Callout } from "nextra/components"; - -# IPv6 and dual-stack - -This guide covers running the [Helm-installed Arcade platform](/operate/deploy/helm) on a dual-stack Kubernetes cluster, and lists the parts that still assume IPv4. +This guide covers running the [Helm-installed Arcade platform](/en/operate/deploy/helm) on a dual-stack Kubernetes cluster, and lists the parts that still assume IPv4. <Callout type="warning"> Dual-stack is the supported way to serve Arcade over IPv6. IPv6-only clusters aren't supported: several parts of the chart and the Arcade Engine still use IPv4-only addresses (see [What doesn't support IPv6](#what-doesnt-support-ipv6)). @@ -59,7 +55,7 @@ Then reach the Arcade Engine's IPv6 ClusterIP from a pod in the cluster, for exa ## What doesn't support IPv6 -These parts are IPv4-only today. Plan around them, especially for [Arcade Deploy](/build/arcade-deploy): +These parts are IPv4-only today. Plan around them, especially for [Arcade Deploy](/en/build/arcade-deploy): - **Deployed MCP servers' outbound traffic.** The NetworkPolicy the Arcade Engine applies to each deployed server allows public egress only to `0.0.0.0/0`, minus private and cluster ranges. There's no IPv6 equivalent, so deployed servers get no IPv6 egress by default. Each `deployments.egressAllowlist` entry becomes its own NetworkPolicy rule, so an IPv6 range listed there may open egress to it, but that path isn't tested. - **The private ranges deployed servers are blocked from** are IPv4 (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `100.64.0.0/10`, `169.254.0.0/16`). Don't put IPv6 ranges in `deployments.network.clusterCIDRs`: they're added as exceptions to the IPv4 `0.0.0.0/0` rule. diff --git a/content/docs/operate/deploy/helm/meta.json b/content/docs/operate/deploy/helm/meta.json new file mode 100644 index 000000000..3c9949c64 --- /dev/null +++ b/content/docs/operate/deploy/helm/meta.json @@ -0,0 +1,9 @@ +{ + "title": "Self-host with Helm", + "pages": [ + "internal-networking", + "ipv6-dual-stack", + "container-registry", + "values-reference" + ] +} diff --git a/app/en/operate/deploy/helm/values-reference/page.mdx b/content/docs/operate/deploy/helm/values-reference.mdx similarity index 99% rename from app/en/operate/deploy/helm/values-reference/page.mdx rename to content/docs/operate/deploy/helm/values-reference.mdx index 1cb4f3546..d844f07fd 100644 --- a/app/en/operate/deploy/helm/values-reference/page.mdx +++ b/content/docs/operate/deploy/helm/values-reference.mdx @@ -1,13 +1,12 @@ --- -title: "Helm values reference" -description: "Every configurable value in the Arcade Helm chart, from released chart version 1.11.2" +title: Helm values reference +sidebarTitle: Values reference +description: Every configurable value in the Arcade Helm chart, from released chart version 1.11.2 --- -# Helm values reference - This reference comes from the README of the released Arcade Helm chart version `1.11.2`. Later chart releases replace it automatically. -Set these values in a `values.yaml` file, or pass them with `--set`, when you install or upgrade the chart. For installation steps, see [Self-host Arcade with Helm](/operate/deploy/helm). +Set these values in a `values.yaml` file, or pass them with `--set`, when you install or upgrade the chart. For installation steps, see [Self-host Arcade with Helm](/en/operate/deploy/helm). Every configurable value, grouped. Defaults are in `values.yaml`. diff --git a/app/en/operate/deploy/page.mdx b/content/docs/operate/deploy/index.mdx similarity index 50% rename from app/en/operate/deploy/page.mdx rename to content/docs/operate/deploy/index.mdx index 3acb1b8db..1cf87ed79 100644 --- a/app/en/operate/deploy/page.mdx +++ b/content/docs/operate/deploy/index.mdx @@ -1,13 +1,11 @@ --- -title: "Overview" -description: "Learn about the different ways to host and deploy Arcade" +title: Hosting options overview +metaTitle: Overview +sidebarTitle: Overview +description: Learn about the different ways to host and deploy Arcade --- -import { Callout } from "nextra/components"; - -# Hosting options overview - -This page is for **platform operators** choosing how to run Arcade for their organization. You can use **Arcade Cloud**, a fully managed service, or **run Arcade in your own infrastructure**. Pick an option below, then continue with the [operator quickstart](/operate/quickstart). +This page is for **platform operators** choosing how to run Arcade for their organization. You can use **Arcade Cloud**, a fully managed service, or **run Arcade in your own infrastructure**. Pick an option below, then continue with the [operator quickstart](/en/operate/quickstart). ## Arcade Cloud @@ -18,7 +16,7 @@ This page is for **platform operators** choosing how to run Arcade for their org - **Built-in scaling**: handles traffic spikes for you - **Free tier**: start building without a credit card -See [Arcade Cloud infrastructure](/operate/deploy/arcade-cloud) for data residency, storage, and security details. +See [Arcade Cloud infrastructure](/en/operate/deploy/arcade-cloud) for data residency, storage, and security details. ## Run Arcade in your own infrastructure @@ -28,13 +26,13 @@ Deploy the full Arcade platform into your own cloud account or cluster when you Deploy a turnkey, Arcade-operated platform into your own cloud account: -- [**Azure Marketplace**](/operate/deploy/azure): available now as a managed app -- [**AWS**](/operate/deploy/aws): available through a private offer -- [**GCP**](/operate/deploy/gcp): marketplace listing in progress +- [**Azure Marketplace**](/en/operate/deploy/azure): available now as a managed app +- [**AWS**](/en/operate/deploy/aws): available through a private offer +- [**GCP**](/en/operate/deploy/gcp): marketplace listing in progress ### Self-host with Helm -Prefer to run it yourself? [Deploy the platform with Helm](/operate/deploy/helm) on your own Kubernetes cluster and manage it end to end. See [Platform architecture](/operate/deploy/architecture) for the services a self-hosted deployment runs. +Prefer to run it yourself? [Deploy the platform with Helm](/en/operate/deploy/helm) on your own Kubernetes cluster and manage it end to end. See [Platform architecture](/en/operate/deploy/architecture) for the services a self-hosted deployment runs. <Callout type="info"> The marketplace and Helm options are **full platform deployments**. The features below run *on top of* Arcade. They aren't ways to stand up the platform. @@ -44,9 +42,9 @@ The marketplace and Helm options are **full platform deployments**. The features These connect your tools and clients to Arcade. They are not platform deployments: -- [**Arcade Deploy**](/build/arcade-deploy): host *your* MCP server with the `arcade deploy` command, on Arcade Cloud or [on your own cluster](/operate/deploy/helm-arcade-deploy) in a self-hosted installation. -- [**Hybrid MCP servers**](/operate/deploy/on-prem): run MCP servers in your own environment and connect them to Arcade Cloud, so tools reach private resources. +- [**Arcade Deploy**](/en/build/arcade-deploy): host *your* MCP server with the `arcade deploy` command, on Arcade Cloud or [on your own cluster](/en/operate/deploy/helm-arcade-deploy) in a self-hosted installation. +- [**Hybrid MCP servers**](/en/operate/deploy/on-prem): run MCP servers in your own environment and connect them to Arcade Cloud, so tools reach private resources. ## Customizing auth -You don't have to self-host to customize your auth experience. Arcade Cloud supports many [auth providers](/references/auth-providers) out of the box, and you can supply your own OAuth app credentials to brand the end-user experience. Arcade recommends this for all production use cases. +You don't have to self-host to customize your auth experience. Arcade Cloud supports many [auth providers](/en/references/auth-providers) out of the box, and you can supply your own OAuth app credentials to brand the end-user experience. Arcade recommends this for all production use cases. diff --git a/content/docs/operate/deploy/meta.json b/content/docs/operate/deploy/meta.json new file mode 100644 index 000000000..828a5b9a4 --- /dev/null +++ b/content/docs/operate/deploy/meta.json @@ -0,0 +1,15 @@ +{ + "title": "Deploy", + "pages": [ + "index", + "architecture", + "arcade-cloud", + "azure", + "aws", + "gcp", + "helm", + "helm-arcade-deploy", + "worker", + "on-prem" + ] +} diff --git a/app/en/operate/deploy/on-prem/page.mdx b/content/docs/operate/deploy/on-prem.mdx similarity index 90% rename from app/en/operate/deploy/on-prem/page.mdx rename to content/docs/operate/deploy/on-prem.mdx index c1d3e474f..2ea2bdf05 100644 --- a/app/en/operate/deploy/on-prem/page.mdx +++ b/content/docs/operate/deploy/on-prem.mdx @@ -1,12 +1,8 @@ --- -title: "Hybrid MCP servers" -description: "Run a self-hosted MCP server worker in your own private network or VPC and connect it to Arcade Cloud. Hybrid worker deployment for on-premises and private cloud environments." +title: Hybrid MCP servers +description: Run a self-hosted MCP server worker in your own private network or VPC and connect it to Arcade Cloud. Hybrid worker deployment for on-premises and private cloud environments. --- -import { Steps, Tabs, Callout } from "nextra/components"; - -# Hybrid MCP servers - <GuideOverview> <GuideOverview.Outcomes> @@ -17,7 +13,7 @@ A hybrid MCP server deployment — also called a self-hosted worker or hybrid wo <GuideOverview.Prerequisites> - <DashboardLink path="register" /> -- [Arcade CLI](/get-started/quickstarts/call-tool-agent) +- [Arcade CLI](/en/get-started/quickstarts/call-tool-agent) - [uv package manager](https://docs.astral.sh/uv/getting-started/installation/) </GuideOverview.Prerequisites> @@ -56,7 +52,7 @@ The on-premises MCP server model (a hybrid worker deployment) uses a bidirection ### Setup your MCP Servers -Follow the [Creating a MCP Server](/build/create-tools/tool-basics/build-mcp-server) guide to create your MCP Server. +Follow the [Creating a MCP Server](/en/build/create-tools/tool-basics/build-mcp-server) guide to create your MCP Server. ### Start your local MCP Server @@ -95,7 +91,7 @@ $env:ARCADE_USER_ID="<your-user-id>" or to a `.env` file: -```env filename=".env" +```env title=".env" ARCADE_API_KEY=<your-api-key> ARCADE_USER_ID=<your-user-id> ``` @@ -304,9 +300,9 @@ You can now test your MCP Server by making requests using the Playground, or an <Tabs.Tab> 1. Use an app that supports MCP clients, like AI assistants and IDEs: - - [Visual Studio Code](/get-started/mcp-clients/visual-studio-code) - - [Claude Desktop](/get-started/mcp-clients/claude-desktop) - - [Cursor](/get-started/mcp-clients/cursor) + - [Visual Studio Code](/en/get-started/mcp-clients/visual-studio-code) + - [Claude Desktop](/en/get-started/mcp-clients/claude-desktop) + - [Cursor](/en/get-started/mcp-clients/cursor) 1. Enable your MCP Server from the list of available MCP Servers 1. Verify that the response is correct and you see request logs in your MCP Server @@ -335,6 +331,6 @@ You can now test your MCP Server by making requests using the Playground, or an ## Next steps -- [Create custom tools](/build/create-tools/tool-basics/build-mcp-server) for your MCP Server -- [Set up authentication](/build/create-tools/tool-basics/create-tool-auth) for secure access to resources -- [Configure secrets](/build/create-tools/tool-basics/create-tool-secrets) for your MCP Server +- [Create custom tools](/en/build/create-tools/tool-basics/build-mcp-server) for your MCP Server +- [Set up authentication](/en/build/create-tools/tool-basics/create-tool-auth) for secure access to resources +- [Configure secrets](/en/build/create-tools/tool-basics/create-tool-secrets) for your MCP Server diff --git a/app/en/operate/deploy/worker/page.mdx b/content/docs/operate/deploy/worker.mdx similarity index 92% rename from app/en/operate/deploy/worker/page.mdx rename to content/docs/operate/deploy/worker.mdx index 76776672a..19ec9f892 100644 --- a/app/en/operate/deploy/worker/page.mdx +++ b/content/docs/operate/deploy/worker.mdx @@ -1,11 +1,9 @@ --- -title: "Arcade Worker" -description: "What the Arcade Worker does in a self-hosted deployment and the settings operators control" +title: Arcade Worker +description: What the Arcade Worker does in a self-hosted deployment and the settings operators control --- -# Arcade Worker - -The Arcade Worker hosts Arcade's toolkits and runs their tool calls for the [Engine](/operate/deploy/architecture#engine). In a self-hosted deployment it's the bundled [MCP server](/operate/deploy/architecture#mcp-servers), and it appears as a `workers[]` entry in the Helm values. +The Arcade Worker hosts Arcade's toolkits and runs their tool calls for the [Engine](/en/operate/deploy/architecture#engine). In a self-hosted deployment it's the bundled [MCP server](/en/operate/deploy/architecture#mcp-servers), and it appears as a `workers[]` entry in the Helm values. This page explains how the worker behaves and when to change each setting. For every value and its default, see the [chart reference on Artifact Hub](https://artifacthub.io/packages/helm/arcade/arcade), which is the source of truth. @@ -80,5 +78,5 @@ Set these environment variables through `extraEnv`. See the [chart reference](ht ## Next steps -- [Self-host with Helm](/operate/deploy/helm) to install the platform -- [Platform architecture](/operate/deploy/architecture) to see how the worker connects to the rest of the platform +- [Self-host with Helm](/en/operate/deploy/helm) to install the platform +- [Platform architecture](/en/operate/deploy/architecture) to see how the worker connects to the rest of the platform diff --git a/app/en/operate/governance/audit-logs/page.mdx b/content/docs/operate/governance/audit-logs.mdx similarity index 97% rename from app/en/operate/governance/audit-logs/page.mdx rename to content/docs/operate/governance/audit-logs.mdx index d08e045cb..b5ec5367e 100644 --- a/app/en/operate/governance/audit-logs/page.mdx +++ b/content/docs/operate/governance/audit-logs.mdx @@ -1,10 +1,10 @@ --- -title: "Audit Logs" -description: "Arcade.dev now captures every administrative action across the platform automatically — who did it, how, what changed, and when." +title: Audit Logs for AI Agent Platforms +metaTitle: Audit Logs +sidebarTitle: Audit Logs +description: Arcade.dev now captures every administrative action across the platform automatically — who did it, how, what changed, and when. --- -# Audit Logs for AI Agent Platforms - TL;DR: Arcade.dev captures every administrative action across the runtime automatically — who did it, how, what changed, and when. There's nothing to enable. Audit logs are on by default, with a filterable dashboard and a REST API for pulling data into your own systems. ![Audit Log Dashboard](/images/screenshots/audit-log.png) diff --git a/app/en/operate/governance/contextual-access/build-your-own/page.mdx b/content/docs/operate/governance/contextual-access/build-your-own.mdx similarity index 91% rename from app/en/operate/governance/contextual-access/build-your-own/page.mdx rename to content/docs/operate/governance/contextual-access/build-your-own.mdx index 26c5dd2a2..e2e43ed30 100644 --- a/app/en/operate/governance/contextual-access/build-your-own/page.mdx +++ b/content/docs/operate/governance/contextual-access/build-your-own.mdx @@ -1,10 +1,10 @@ --- -title: "Build Your Own" -description: "Build your own Logic Extensions webhook server from the OpenAPI spec" +title: Build Your Own Contextual Access Server +metaTitle: Build Your Own +sidebarTitle: Build Your Own +description: Build your own Logic Extensions webhook server from the OpenAPI spec --- -# Build Your Own Contextual Access Server - This guide walks through implementing a webhook server that satisfies the Contextual Access Webhook contract. You can write it in any language — the contract is defined by an OpenAPI 3.0 spec. ## OpenAPI spec @@ -13,7 +13,7 @@ The canonical contract is maintained in the [ArcadeAI/schemas](https://github.co - **OpenAPI 3.0 spec:** [logic_extensions/http/1.0/schema.yaml](https://github.com/ArcadeAI/schemas/blob/main/logic_extensions/http/1.0/schema.yaml) -You can browse it interactively on the [API Reference](/references/contextual-access-webhook-api) page. +You can browse it interactively on the [API Reference](/en/references/contextual-access-webhook-api) page. ## Generate server stubs @@ -127,7 +127,7 @@ Tools can include an optional `metadata` field with classification, behavioral, All metadata fields are optional and omitted from the JSON when empty, so existing webhook servers continue to work without changes. -For the full list of valid `service_domains` and `operations` values, see [Add Tool Metadata](/build/create-tools/tool-basics/add-tool-metadata). +For the full list of valid `service_domains` and `operations` values, see [Add Tool Metadata](/en/build/create-tools/tool-basics/add-tool-metadata). ### Structure @@ -222,7 +222,7 @@ Webhook servers can use tool metadata to make more granular decisions: | `CHECK_FAILED` | Deny the operation; `error_message` is shown to the agent | | `RATE_LIMIT_EXCEEDED` | Deny with rate-limit semantics | -For per-tool call caps that need no webhook server, Arcade also enforces [built-in rate limits](/operate/governance/contextual-access/rate-limiting). +For per-tool call caps that need no webhook server, Arcade also enforces [built-in rate limits](/en/operate/governance/contextual-access/rate-limiting). ## Authentication @@ -241,6 +241,6 @@ Configure the auth method when creating your extension in the Dashboard. ## Next steps -- [API Reference](/references/contextual-access-webhook-api) — Interactive Swagger documentation for the full schema -- [Run an extension](/operate/governance/contextual-access/examples) — Try the open-source example servers as reference -- [How Hooks Work](/operate/governance/contextual-access/how-hooks-work) — Understand execution order, phases, and failure modes +- [API Reference](/en/references/contextual-access-webhook-api) — Interactive Swagger documentation for the full schema +- [Run an extension](/en/operate/governance/contextual-access/examples) — Try the open-source example servers as reference +- [How Hooks Work](/en/operate/governance/contextual-access/how-hooks-work) — Understand execution order, phases, and failure modes diff --git a/app/en/operate/governance/contextual-access/examples/page.mdx b/content/docs/operate/governance/contextual-access/examples.mdx similarity index 86% rename from app/en/operate/governance/contextual-access/examples/page.mdx rename to content/docs/operate/governance/contextual-access/examples.mdx index 692200220..71ef82670 100644 --- a/app/en/operate/governance/contextual-access/examples/page.mdx +++ b/content/docs/operate/governance/contextual-access/examples.mdx @@ -1,10 +1,9 @@ --- -title: "Running a Server" -description: "Run open-source example webhook servers to get started with Contextual Access" +title: Running a Server +sidebarTitle: Running an Extension +description: Run open-source example webhook servers to get started with Contextual Access --- -# Running a Server - The fastest way to get started with Contextual Access is to run one of the open-source example servers. These are example Go implementations you can use as-is or as a starting point for your own server. ## Example servers @@ -75,9 +74,9 @@ Once your server is running: 2. Click **Create Extension** and enter your server's base URL and endpoint paths 3. Create **hook configurations** to attach the extension to the hook points you want -See [How Hooks Work](/operate/governance/contextual-access/how-hooks-work) for details on configuring extensions and hook points. +See [How Hooks Work](/en/operate/governance/contextual-access/how-hooks-work) for details on configuring extensions and hook points. ## Next steps -- [Build your own](/operate/governance/contextual-access/build-your-own) — Implement the webhook contract in any language -- [API Reference](/references/contextual-access-webhook-api) — Interactive schema documentation for the webhook contract +- [Build your own](/en/operate/governance/contextual-access/build-your-own) — Implement the webhook contract in any language +- [API Reference](/en/references/contextual-access-webhook-api) — Interactive schema documentation for the webhook contract diff --git a/app/en/operate/governance/contextual-access/group-access/page.mdx b/content/docs/operate/governance/contextual-access/group-access.mdx similarity index 91% rename from app/en/operate/governance/contextual-access/group-access/page.mdx rename to content/docs/operate/governance/contextual-access/group-access.mdx index 1f52ed78a..be357e131 100644 --- a/app/en/operate/governance/contextual-access/group-access/page.mdx +++ b/content/docs/operate/governance/contextual-access/group-access.mdx @@ -1,13 +1,9 @@ --- -title: "Group-Based Tool Access" -description: "Vary which tools a caller can see and use based on their identity provider group membership, enforced natively by the Arcade Engine" +title: Group-Based Tool Access +description: Vary which tools a caller can see and use based on their identity provider group membership, enforced natively by the Arcade Engine --- -import { Callout, Steps } from "nextra/components"; - -# Group-Based Tool Access - -As a platform operator, you can vary which tools a caller sees based on their identity provider groups. Group-based access is built into the Arcade Engine: you define rules that require membership in a group, and the Engine checks membership and filters the tool list before the caller sees it. Like [rate limits](/operate/governance/contextual-access/rate-limiting), it runs natively inside Arcade, so there is no server to build or host. +As a platform operator, you can vary which tools a caller sees based on their identity provider groups. Group-based access is built into the Arcade Engine: you define rules that require membership in a group, and the Engine checks membership and filters the tool list before the caller sees it. Like [rate limits](/en/operate/governance/contextual-access/rate-limiting), it runs natively inside Arcade, so there is no server to build or host. Use it when one MCP gateway serves more than one team or customer and they should not all see the same tools, instead of running a separate gateway for each. @@ -185,7 +181,7 @@ To use another backend, replace the `okta` block with an `entra` or `sailpoint` { "backend": "sailpoint", "sailpoint": { "tenant_url": "https://acme.identitynow.com", "client_id": "2c9180835d2e5168015d32f890ca1581", "client_secret": "..." } } ``` -To bind the configuration to the organization instead of a project, post to `/v1/orgs/{org_id}/plugins`. The [API reference](/references/api) documents the full plugins API, including listing, updating, and deleting. +To bind the configuration to the organization instead of a project, post to `/v1/orgs/{org_id}/plugins`. The [API reference](/en/references/api) documents the full plugins API, including listing, updating, and deleting. When you update a configuration, the rules you send replace the existing rules, so send the complete list. A credential you leave out keeps its stored value, unless the update changes the backend, a URL, the tenant ID, or the client ID, in which case the credential is required. @@ -214,7 +210,7 @@ To show the tool instead when membership cannot be verified, turn on **Show the ## Next steps -- [How hooks work](/operate/governance/contextual-access/how-hooks-work) - Where group access fits in the hook pipeline -- [Rate limiting](/operate/governance/contextual-access/rate-limiting) - Cap how often tools can be called -- [Build your own](/operate/governance/contextual-access/build-your-own) - Enforce custom policies from your own webhook server -- [API reference](/references/api) - Full plugins API documentation +- [How hooks work](/en/operate/governance/contextual-access/how-hooks-work) - Where group access fits in the hook pipeline +- [Rate limiting](/en/operate/governance/contextual-access/rate-limiting) - Cap how often tools can be called +- [Build your own](/en/operate/governance/contextual-access/build-your-own) - Enforce custom policies from your own webhook server +- [API reference](/en/references/api) - Full plugins API documentation diff --git a/app/en/operate/governance/contextual-access/how-hooks-work/page.mdx b/content/docs/operate/governance/contextual-access/how-hooks-work.mdx similarity index 89% rename from app/en/operate/governance/contextual-access/how-hooks-work/page.mdx rename to content/docs/operate/governance/contextual-access/how-hooks-work.mdx index 49e5baaa6..6ffe61b62 100644 --- a/app/en/operate/governance/contextual-access/how-hooks-work/page.mdx +++ b/content/docs/operate/governance/contextual-access/how-hooks-work.mdx @@ -1,10 +1,8 @@ --- -title: "How Hooks Work" -description: "Understand hook points, execution order, extensions, failure modes, and how the Arcade calls your webhook server" +title: How Hooks Work +description: Understand hook points, execution order, extensions, failure modes, and how the Arcade calls your webhook server --- -# How Hooks Work - This page explains how Arcade invokes your external logic at each stage of tool execution, how you configure extensions and hook points, and how ordering and failures are handled. ## Hook points @@ -87,6 +85,6 @@ You configure extensions and hook points from the **Arcade Dashboard**: ## Next steps -- [Run an extension](/operate/governance/contextual-access/examples) — Try the open-source example servers -- [Build your own](/operate/governance/contextual-access/build-your-own) — Implement the webhook contract from the OpenAPI spec -- [API Reference](/references/contextual-access-webhook-api) — Interactive schema documentation for the webhook contract +- [Run an extension](/en/operate/governance/contextual-access/examples) — Try the open-source example servers +- [Build your own](/en/operate/governance/contextual-access/build-your-own) — Implement the webhook contract from the OpenAPI spec +- [API Reference](/en/references/contextual-access-webhook-api) — Interactive schema documentation for the webhook contract diff --git a/app/en/operate/governance/contextual-access/page.mdx b/content/docs/operate/governance/contextual-access/index.mdx similarity index 78% rename from app/en/operate/governance/contextual-access/page.mdx rename to content/docs/operate/governance/contextual-access/index.mdx index 8df31f9c2..af179a336 100644 --- a/app/en/operate/governance/contextual-access/page.mdx +++ b/content/docs/operate/governance/contextual-access/index.mdx @@ -1,12 +1,8 @@ --- -title: "Contextual Access" -description: "Control who can see and use which tools, validate requests, and filter outputs — per user, per org, per project" +title: Contextual Access +description: Control who can see and use which tools, validate requests, and filter outputs — per user, per org, per project --- -import { Cards } from "nextra/components"; - -# Contextual Access - Agents have broad access to tools, but enterprises need fine-grained control. Arcade's contextual access lets you govern tool visibility and behavior at every stage of execution — who can see a tool, what inputs are allowed, and what comes back. You connect your own access-control, compliance, or transformation logic to the Arcade Engine. Your rules run inline during tool execution with no changes to your tools or agents required. @@ -37,22 +33,22 @@ You only implement the hooks you need. Configure everything through the Dashboar <Cards> <Cards.Card title="How Hooks Work" - href="/operate/governance/contextual-access/how-hooks-work" + href="/en/operate/governance/contextual-access/how-hooks-work" /> <Cards.Card title="Rate Limiting" - href="/operate/governance/contextual-access/rate-limiting" + href="/en/operate/governance/contextual-access/rate-limiting" /> <Cards.Card title="Run an Extension" - href="/operate/governance/contextual-access/examples" + href="/en/operate/governance/contextual-access/examples" /> <Cards.Card title="Build Your Own" - href="/operate/governance/contextual-access/build-your-own" + href="/en/operate/governance/contextual-access/build-your-own" /> <Cards.Card title="API Reference" - href="/references/contextual-access-webhook-api" + href="/en/references/contextual-access-webhook-api" /> </Cards> diff --git a/content/docs/operate/governance/contextual-access/meta.json b/content/docs/operate/governance/contextual-access/meta.json new file mode 100644 index 000000000..de9e9fe2a --- /dev/null +++ b/content/docs/operate/governance/contextual-access/meta.json @@ -0,0 +1,10 @@ +{ + "title": "Contextual Access", + "pages": [ + "how-hooks-work", + "rate-limiting", + "group-access", + "examples", + "build-your-own" + ] +} diff --git a/app/en/operate/governance/contextual-access/rate-limiting/page.mdx b/content/docs/operate/governance/contextual-access/rate-limiting.mdx similarity index 87% rename from app/en/operate/governance/contextual-access/rate-limiting/page.mdx rename to content/docs/operate/governance/contextual-access/rate-limiting.mdx index 077cdf87c..b5e54be83 100644 --- a/app/en/operate/governance/contextual-access/rate-limiting/page.mdx +++ b/content/docs/operate/governance/contextual-access/rate-limiting.mdx @@ -1,12 +1,8 @@ --- -title: "Rate Limiting" -description: "Cap how often tools can be called with built-in rate limits enforced by the Arcade Engine, no webhook server required" +title: Rate Limiting +description: Cap how often tools can be called with built-in rate limits enforced by the Arcade Engine, no webhook server required --- -import { Callout, Steps } from "nextra/components"; - -# Rate Limiting - As a platform operator, you can cap how often tools are called. Rate limits are built into the Arcade Engine: you define rules in the Dashboard or through the API, and the Engine enforces them before each tool execution. Unlike webhook extensions, rate limits run natively inside Arcade, so there is no server to build or host. Use rate limits to protect upstream services from runaway agents, keep automated workloads inside vendor quotas, and contain the blast radius of a misbehaving loop. @@ -100,7 +96,7 @@ curl -s -X POST "https://api.arcade.dev/v1/orgs/{org_id}/projects/{project_id}/p }' ``` -To bind a rate limit to the organization instead of a project, post to `/v1/orgs/{org_id}/plugins`. The [API reference](/references/api) documents the full plugins API, including listing, updating, and deleting. +To bind a rate limit to the organization instead of a project, post to `/v1/orgs/{org_id}/plugins`. The [API reference](/en/references/api) documents the full plugins API, including listing, updating, and deleting. ## When the platform cannot verify a limit @@ -125,6 +121,6 @@ The rate limit for Slack.SendMessage could not be verified; the call was rejecte ## Next steps -- [How hooks work](/operate/governance/contextual-access/how-hooks-work) - Where rate limits fit in the hook pipeline -- [Build your own](/operate/governance/contextual-access/build-your-own) - Enforce custom policies, including rate-limit responses, from your own webhook server -- [API reference](/references/api) - Full plugins API documentation +- [How hooks work](/en/operate/governance/contextual-access/how-hooks-work) - Where rate limits fit in the hook pipeline +- [Build your own](/en/operate/governance/contextual-access/build-your-own) - Enforce custom policies, including rate-limit responses, from your own webhook server +- [API reference](/en/references/api) - Full plugins API documentation diff --git a/content/docs/operate/governance/index.mdx b/content/docs/operate/governance/index.mdx new file mode 100644 index 000000000..e5a81bf71 --- /dev/null +++ b/content/docs/operate/governance/index.mdx @@ -0,0 +1,28 @@ +--- +title: Governance +sidebarTitle: Overview +description: Federate tools, enforce access policies, and review administrative actions and tool runs +--- + +Control which tools agents can use, enforce policies at runtime, and review administrative actions and the tool runs they produced. + +<Cards num={2}> + <Cards.Card title="MCP Gateways" href="/en/operate/governance/mcp-gateways" /> + <Cards.Card + title="Remote MCP servers" + href="/en/operate/governance/remote-mcp-servers" + /> + <Cards.Card + title="Private Registry" + href="/en/operate/governance/private-registry" + /> + <Cards.Card + title="Contextual Access" + href="/en/operate/governance/contextual-access" + /> + <Cards.Card title="Audit Logs" href="/en/operate/governance/audit-logs" /> + <Cards.Card + title="Tool Executions" + href="/en/operate/governance/tool-executions" + /> +</Cards> diff --git a/app/en/operate/governance/mcp-gateways/create-via-ai/page.mdx b/content/docs/operate/governance/mcp-gateways/create-via-ai.mdx similarity index 77% rename from app/en/operate/governance/mcp-gateways/create-via-ai/page.mdx rename to content/docs/operate/governance/mcp-gateways/create-via-ai.mdx index 1f990486f..4d12ee729 100644 --- a/app/en/operate/governance/mcp-gateways/create-via-ai/page.mdx +++ b/content/docs/operate/governance/mcp-gateways/create-via-ai.mdx @@ -1,12 +1,10 @@ --- -title: "Arcade Gateway Assistant" -description: "Create and manage MCP Gateways using AI directly from your chat interface" +title: Arcade Gateway Assistant +sidebarTitle: Create via AI Assistant +description: Create and manage MCP Gateways using AI directly from your chat interface --- -import { Callout, Steps } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; - -# Arcade Gateway Assistant +import { SignupLink } from "@/components/analytics"; <GuideOverview> <GuideOverview.Outcomes> @@ -18,7 +16,7 @@ Create and manage MCP gateways directly from your chat interface using natural l <GuideOverview.Prerequisites> 1. Create an <SignupLink linkLocation="docs:gateway-assistant">Arcade account</SignupLink> -2. An MCP-compatible chat client ([Cursor](/get-started/mcp-clients/cursor), [Claude Desktop](/get-started/mcp-clients/claude-desktop), [VS Code](/get-started/mcp-clients/visual-studio-code), etc.) +2. An MCP-compatible chat client ([Cursor](/en/get-started/mcp-clients/cursor), [Claude Desktop](/en/get-started/mcp-clients/claude-desktop), [VS Code](/en/get-started/mcp-clients/visual-studio-code), etc.) </GuideOverview.Prerequisites> @@ -40,7 +38,7 @@ https://ctl.arcade.dev/mcp The Gateway Assistant uses **Arcade Auth**. This means you'll authenticate with your Arcade account to access the assistant. </Callout> -Each MCP client has a different setup process. See [Connect to MCP clients](/get-started/mcp-clients) for detailed instructions for adding the Gateway Assistant to Cursor, Claude Desktop, VS Code, and other supported clients with Arcade Auth. +Each MCP client has a different setup process. See [Connect to MCP clients](/en/get-started/mcp-clients) for detailed instructions for adding the Gateway Assistant to Cursor, Claude Desktop, VS Code, and other supported clients with Arcade Auth. ### Authenticate @@ -70,7 +68,7 @@ The assistant will select the appropriate tools from Arcade's catalog and create Once you've created a gateway, you'll need to add it to your chat client as a separate MCP server. The assistant will provide your new gateway's MCP URL (for example, `https://api.arcade.dev/mcp/<YOUR-GATEWAY-SLUG>`). -Follow the same process you used to add the Gateway Assistant - see [Connect to MCP clients](/get-started/mcp-clients) for setup instructions specific to your client. +Follow the same process you used to add the Gateway Assistant - see [Connect to MCP clients](/en/get-started/mcp-clients) for setup instructions specific to your client. <Callout type="info"> The Gateway Assistant creates gateways that use **Arcade Auth** by default. This means you'll authenticate with your Arcade account to access the gateway. For production use cases with end users who don't have Arcade accounts, you can modify the gateway's authentication settings in the [dashboard](https://api.arcade.dev/dashboard/mcp-gateways). diff --git a/app/en/operate/governance/mcp-gateways/create-via-dashboard/page.mdx b/content/docs/operate/governance/mcp-gateways/create-via-dashboard.mdx similarity index 81% rename from app/en/operate/governance/mcp-gateways/create-via-dashboard/page.mdx rename to content/docs/operate/governance/mcp-gateways/create-via-dashboard.mdx index f4c369e64..35881bc0a 100644 --- a/app/en/operate/governance/mcp-gateways/create-via-dashboard/page.mdx +++ b/content/docs/operate/governance/mcp-gateways/create-via-dashboard.mdx @@ -1,11 +1,10 @@ --- -title: "Create via Dashboard" -description: "Create and configure MCP Gateways using the Arcade dashboard" +title: Create via Dashboard +description: Create and configure MCP Gateways using the Arcade dashboard --- -import { Callout } from "nextra/components"; import Image from "next/image"; -import { SignupLink } from "@/app/_components/analytics"; +import { SignupLink } from "@/components/analytics"; export const IMAGE_SCALE_FACTOR = 2; export const TOOL_FILTER_LIGHT_WIDTH = 2052; @@ -13,8 +12,6 @@ export const TOOL_FILTER_LIGHT_HEIGHT = 1412; export const TOOL_FILTER_DARK_WIDTH = 2052; export const TOOL_FILTER_DARK_HEIGHT = 1412; -# Create via Dashboard - <GuideOverview> <GuideOverview.Outcomes> @@ -62,18 +59,18 @@ The options available when configuring an MCP Gateway are: - **Authentication**: How end users authenticate to the gateway. Users will still need to authenticate to the tools within the MCP Gateway as normal. The form asks "Who are the users of this Gateway?" with two top-level choices: - **Members of this Project (Arcade Auth)**: End users sign in with Arcade as members of this project. Recommended for development, testing, and internal use when every end user is already a member. - **Non-Arcade Users**: End users come from outside Arcade. Pick one of: - - **User Source** (recommended): Arcade redirects end users to your OIDC identity provider to sign in. Pick this for production agents whose end users have identities in your own OIDC identity system. See [User Sources](/operate/identity/user-sources) for how to set one up. + - **User Source** (recommended): Arcade redirects end users to your OIDC identity provider to sign in. Pick this for production agents whose end users have identities in your own OIDC identity system. See [User Sources](/en/operate/identity/user-sources) for how to set one up. - **Arcade Headers** (fallback): The client passes an Arcade API key in the `Authorization` header and the end user's ID in the `Arcade-User-ID` header. Pick this for MCP clients that can't run a browser-based OAuth flow. - **Allowed Tools**: A selection of tools in the Arcade Tool Catalog that will be available to the MCP Gateway. -- **Capability requests**: On by default. Lets end users and their agents request missing capabilities, which land in your project's **Inbox**. See [Private Registry](/operate/governance/private-registry#turn-capability-requests-on-or-off). +- **Capability requests**: On by default. Lets end users and their agents request missing capabilities, which land in your project's **Inbox**. See [Private Registry](/en/operate/governance/private-registry#turn-capability-requests-on-or-off). -See [Skip consent for trusted MCP clients](/operate/governance/mcp-gateways#skip-consent-for-trusted-mcp-clients) for the **Skip consent for trusted clients (optional)** field on this same form. +See [Skip consent for trusted MCP clients](/en/operate/governance/mcp-gateways#skip-consent-for-trusted-mcp-clients) for the **Skip consent for trusted clients (optional)** field on this same form. ## After Creating a Gateway Once you've created a gateway, add it to your MCP client. The dashboard shows the MCP URL (for example, `https://api.arcade.dev/mcp/{YOUR-GATEWAY-SLUG}`). -See [Connect to MCP clients](/get-started/mcp-clients) for setup instructions specific to your client. +See [Connect to MCP clients](/en/get-started/mcp-clients) for setup instructions specific to your client. ### Point Claude at the gateway @@ -84,10 +81,10 @@ Add the gateway URL as a custom connector. Claude shows **Authentication** and * - **Sign in now** for Authentication - **Use Claude's published identity** for OAuth client -On Claude Team and Enterprise plans, only an Owner can add the connector. Members then click **Connect**. See [Keep the detected defaults](/get-started/mcp-clients/claude-desktop#keep-the-detected-defaults). +On Claude Team and Enterprise plans, only an Owner can add the connector. Members then click **Connect**. See [Keep the detected defaults](/en/get-started/mcp-clients/claude-desktop#keep-the-detected-defaults). <Callout type="info"> To skip Arcade's consent screen for Claude users, allowlist Claude's published identity on the gateway. See [Skip consent for trusted MCP - clients](/operate/governance/mcp-gateways#skip-consent-for-trusted-mcp-clients). + clients](/en/operate/governance/mcp-gateways#skip-consent-for-trusted-mcp-clients). </Callout> diff --git a/app/en/operate/governance/mcp-gateways/page.mdx b/content/docs/operate/governance/mcp-gateways/index.mdx similarity index 82% rename from app/en/operate/governance/mcp-gateways/page.mdx rename to content/docs/operate/governance/mcp-gateways/index.mdx index 80f2cda54..f70b5869b 100644 --- a/app/en/operate/governance/mcp-gateways/page.mdx +++ b/content/docs/operate/governance/mcp-gateways/index.mdx @@ -1,12 +1,9 @@ --- -title: "MCP Gateways" -description: "Connect multiple MCP servers to your agent, application, or IDE with Arcade MCP Gateways" +title: MCP Gateways +description: Connect multiple MCP servers to your agent, application, or IDE with Arcade MCP Gateways --- -import { Cards } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; - -# MCP Gateways +import { SignupLink } from "@/components/analytics"; MCP Gateways are a way to connect multiple MCP Servers to your agent, application, or IDE. MCP Gateways allow you to federate the tools from multiple MCP Servers into a single collection for easier management, control, and access. You can mix and match tools from different MCP Servers in the same project. @@ -25,11 +22,11 @@ You can create an MCP Gateway in two ways: <Cards> <Cards.Card title="Create via Dashboard" - href="/operate/governance/mcp-gateways/create-via-dashboard" + href="/en/operate/governance/mcp-gateways/create-via-dashboard" /> <Cards.Card title="Create via AI Assistant" - href="/operate/governance/mcp-gateways/create-via-ai" + href="/en/operate/governance/mcp-gateways/create-via-ai" /> </Cards> @@ -45,7 +42,7 @@ A gateway can federate tools from any server in scope, including MCP servers Arc <Cards> <Cards.Card title="Remote MCP servers" - href="/operate/governance/remote-mcp-servers" + href="/en/operate/governance/remote-mcp-servers" /> </Cards> @@ -56,7 +53,7 @@ A gateway with many tools can overwhelm an agent's context. With Tool Recommenda <Cards> <Cards.Card title="Tool Recommendation" - href="/operate/governance/mcp-gateways/tool-recommendation" + href="/en/operate/governance/mcp-gateways/tool-recommendation" /> </Cards> @@ -68,9 +65,9 @@ Any MCP client that supports the Streamable HTTP transport can use an Arcade MCP https://api.arcade.dev/mcp/{YOUR-GATEWAY-SLUG} ``` -Learn how to [connect MCP Gateways to your preferred client](/get-started/mcp-clients). +Learn how to [connect MCP Gateways to your preferred client](/en/get-started/mcp-clients). -Opening the same URL in a browser shows the gateway's [Private Registry](/operate/governance/private-registry), where end users browse the gateway's tools and request missing ones. +Opening the same URL in a browser shows the gateway's [Private Registry](/en/operate/governance/private-registry), where end users browse the gateway's tools and request missing ones. ## Authentication @@ -82,7 +79,7 @@ When you create a gateway, you choose who its end users are. Arcade groups the o | **User Source** (recommended for production) | Production agents whose end users have identities in your OIDC identity provider | Arcade redirects end users to your identity provider, then identifies them by a configured subject claim | | **Arcade Headers** (fallback) | MCP clients that can't run a browser-based OAuth flow | The client passes `Authorization: Bearer {your_api_key}` and `Arcade-User-ID: {end_user_id}` on every request | -See [User Sources](/operate/identity/user-sources) for how to set up an OIDC identity provider and attach it to a gateway. See [Create via Dashboard](/operate/governance/mcp-gateways/create-via-dashboard) for the rest of the gateway configuration. +See [User Sources](/en/operate/identity/user-sources) for how to set up an OIDC identity provider and attach it to a gateway. See [Create via Dashboard](/en/operate/governance/mcp-gateways/create-via-dashboard) for the rest of the gateway configuration. ## Skip consent for trusted MCP clients @@ -104,7 +101,7 @@ The values below are the literal `client_id` strings these clients send in their When you add an Arcade gateway as a custom connector in Claude, keep **Use Claude's published identity**. That option sends the Claude CIMD URL from the preceding table. Allowlist the same URL if you want Claude users to skip Arcade's consent screen. Do not switch Claude to **Register automatically** if you want to skip the consent screen. Dynamic Client Registration does not produce a stable client ID. -See [Keep the detected defaults](/get-started/mcp-clients/claude-desktop#keep-the-detected-defaults) for the rest of the Claude connector options. +See [Keep the detected defaults](/en/get-started/mcp-clients/claude-desktop#keep-the-detected-defaults) for the rest of the Claude connector options. ### Find the client ID for an unlisted client @@ -115,4 +112,4 @@ If the client uses Dynamic Client Registration, it gets a new client ID per regi ## Next Steps - <SignupLink linkLocation="docs:mcp-gateways">Create an Arcade account</SignupLink> if you haven't already -- [Browse available integrations](/resources/integrations) to see what tools you can add to your gateway +- [Browse available integrations](/en/resources/integrations) to see what tools you can add to your gateway diff --git a/content/docs/operate/governance/mcp-gateways/meta.json b/content/docs/operate/governance/mcp-gateways/meta.json new file mode 100644 index 000000000..a7938b8f2 --- /dev/null +++ b/content/docs/operate/governance/mcp-gateways/meta.json @@ -0,0 +1,4 @@ +{ + "title": "MCP Gateways", + "pages": ["create-via-dashboard", "create-via-ai", "tool-recommendation"] +} diff --git a/app/en/operate/governance/mcp-gateways/tool-recommendation/configure/page.mdx b/content/docs/operate/governance/mcp-gateways/tool-recommendation/configure.mdx similarity index 82% rename from app/en/operate/governance/mcp-gateways/tool-recommendation/configure/page.mdx rename to content/docs/operate/governance/mcp-gateways/tool-recommendation/configure.mdx index 3930ab784..21df05f4b 100644 --- a/app/en/operate/governance/mcp-gateways/tool-recommendation/configure/page.mdx +++ b/content/docs/operate/governance/mcp-gateways/tool-recommendation/configure.mdx @@ -1,13 +1,10 @@ --- -title: "Configure Tool Recommendation" -description: "Turn Tool Recommendation on or off for an MCP Gateway, and understand how it interacts with the gateway's allowed tools" +title: Configure Tool Recommendation +sidebarTitle: Configure +description: Turn Tool Recommendation on or off for an MCP Gateway, and understand how it interacts with the gateway's allowed tools --- -import { Callout } from "nextra/components"; - -# Configure Tool Recommendation - -This page is for platform operators who manage MCP Gateways. It shows how to turn [Tool Recommendation](/operate/governance/mcp-gateways/tool-recommendation) on or off for a gateway, how it interacts with the gateway's allowed tools, and what a caller gets when it is off. Use it when a gateway has grown past the point where an agent can hold every tool in context. +This page is for platform operators who manage MCP Gateways. It shows how to turn [Tool Recommendation](/en/operate/governance/mcp-gateways/tool-recommendation) on or off for a gateway, how it interacts with the gateway's allowed tools, and what a caller gets when it is off. Use it when a gateway has grown past the point where an agent can hold every tool in context. Tool Recommendation is a setting on each gateway. Turning it on for one gateway does not change any other gateway in the project. @@ -15,9 +12,9 @@ New gateways have Tool Recommendation on by default for organizations with Tool ## Prerequisites -- An MCP Gateway, or permission to create one. See [Create via Dashboard](/operate/governance/mcp-gateways/create-via-dashboard). +- An MCP Gateway, or permission to create one. See [Create via Dashboard](/en/operate/governance/mcp-gateways/create-via-dashboard). - Tool Recommendation enabled for your deployment. - - **Arcade Cloud:** Tool Recommendation is enabled by default for organizations. If the **Tool Recommendations** option does not appear on the gateway form, [contact Arcade](/resources/contact-us) to enable it for your organization. + - **Arcade Cloud:** Tool Recommendation is enabled by default for organizations. If the **Tool Recommendations** option does not appear on the gateway form, [contact Arcade](/en/resources/contact-us) to enable it for your organization. - **Self-hosted:** Tool Recommendation needs to be enabled for your deployment. [Contact sales](https://www.arcade.dev/meet/contact-sales) to enable it. ## Turn it on in the dashboard @@ -54,7 +51,7 @@ The gateway response reports the current setting in `tool_filter.discovery.enabl The gateway accepts this setting even when Tool Recommendation is off for your organization. In that case the setting has no effect, and the gateway behaves as if it were off. -See the [Arcade API reference](/references/api) for the full gateway schema. +See the [Arcade API reference](/en/references/api) for the full gateway schema. ## Choose which tools Arcade can recommend @@ -97,5 +94,5 @@ If the list still shows every allowed tool: ## Next steps -- Read [Tool Recommendation limitations](/operate/governance/mcp-gateways/tool-recommendation/limitations) before you turn it on for every gateway. -- Improve recommendation quality for your own tools. See [What makes recommendations good](/operate/governance/mcp-gateways/tool-recommendation#what-makes-recommendations-good). +- Read [Tool Recommendation limitations](/en/operate/governance/mcp-gateways/tool-recommendation/limitations) before you turn it on for every gateway. +- Improve recommendation quality for your own tools. See [What makes recommendations good](/en/operate/governance/mcp-gateways/tool-recommendation#what-makes-recommendations-good). diff --git a/app/en/operate/governance/mcp-gateways/tool-recommendation/page.mdx b/content/docs/operate/governance/mcp-gateways/tool-recommendation/index.mdx similarity index 88% rename from app/en/operate/governance/mcp-gateways/tool-recommendation/page.mdx rename to content/docs/operate/governance/mcp-gateways/tool-recommendation/index.mdx index aa204e9e5..7e1fa7eae 100644 --- a/app/en/operate/governance/mcp-gateways/tool-recommendation/page.mdx +++ b/content/docs/operate/governance/mcp-gateways/tool-recommendation/index.mdx @@ -1,13 +1,10 @@ --- -title: "Tool Recommendation" -description: "How Tool Recommendation lets an agent ask an MCP Gateway for the tools that fit a task instead of loading every tool into context" +title: Tool Recommendation +sidebarTitle: Overview +description: How Tool Recommendation lets an agent ask an MCP Gateway for the tools that fit a task instead of loading every tool into context --- -import { Cards } from "nextra/components"; - -# Tool Recommendation - -Tool Recommendation lets an agent describe a task and get back only the tools that fit it, instead of holding every tool on a gateway in its context. This page is for agent developers and platform operators who run gateways with many tools. It explains what an MCP client gets when Tool Recommendation is on, how the agent finds and calls tools, and what decides the quality of the results. Read it before you [turn Tool Recommendation on](/operate/governance/mcp-gateways/tool-recommendation/configure) for a gateway. +Tool Recommendation lets an agent describe a task and get back only the tools that fit it, instead of holding every tool on a gateway in its context. This page is for agent developers and platform operators who run gateways with many tools. It explains what an MCP client gets when Tool Recommendation is on, how the agent finds and calls tools, and what decides the quality of the results. Read it before you [turn Tool Recommendation on](/en/operate/governance/mcp-gateways/tool-recommendation/configure) for a gateway. ## Why it exists @@ -71,11 +68,11 @@ The tool descriptions that Arcade serves tell the model how to use this flow, so Tool Recommendation doesn't change which tools an agent can use. `Arcade.SelectTools` returns only tools that the gateway allows, and `Arcade.UseTool` runs any tool that the gateway allows, whether or not `Arcade.SelectTools` returned it. -Policy enforcement and authorization work the same way with Tool Recommendation on or off. See [Contextual Access](/operate/governance/contextual-access) for how access rules decide which tools each user can see and use, and [Authorized Tool Calling](/build/tool-calling/custom-apps/auth-tool-calling) for how tools run as the end user. +Policy enforcement and authorization work the same way with Tool Recommendation on or off. See [Contextual Access](/en/operate/governance/contextual-access) for how access rules decide which tools each user can see and use, and [Authorized Tool Calling](/en/build/tool-calling/custom-apps/auth-tool-calling) for how tools run as the end user. ## Use it over the REST API -Agents and apps that don't connect through MCP can call the same operations as REST endpoints of the [Arcade API](/references/api): +Agents and apps that don't connect through MCP can call the same operations as REST endpoints of the [Arcade API](/en/references/api): | Endpoint | MCP tool | What it does | | --- | --- | --- | @@ -155,7 +152,7 @@ An empty `suggestions` list means no tool the user may use matched that task. If } ``` -This endpoint doesn't take a gateway, so it isn't limited to what `select` returned or to one gateway's tools. It can run any tool in the project that the user can access, and your access rules apply to every call. See [Authorized Tool Calling](/build/tool-calling/custom-apps/auth-tool-calling) for what happens when a tool needs the user to authorize first. +This endpoint doesn't take a gateway, so it isn't limited to what `select` returned or to one gateway's tools. It can run any tool in the project that the user can access, and your access rules apply to every call. See [Authorized Tool Calling](/en/build/tool-calling/custom-apps/auth-tool-calling) for what happens when a tool needs the user to authorize first. ### List apps @@ -171,7 +168,7 @@ Tool Recommendation runs inside the gateway and speaks standard MCP. The Arcade Arcade matches a task against each tool's name and description. The input parameters are not part of the match, and Arcade reads only the first line of each description. -That makes tool descriptions the main factor you control. A tool with a vague or empty first line is hard to recommend, however useful the tool is. When you [build your own tools](/build/create-tools) or register a [remote MCP server](/operate/governance/remote-mcp-servers): +That makes tool descriptions the main factor you control. A tool with a vague or empty first line is hard to recommend, however useful the tool is. When you [build your own tools](/en/build/create-tools) or register a [remote MCP server](/en/operate/governance/remote-mcp-servers): - Start each description with one sentence that says what the tool does and to what, for example "Send a message to a Slack channel or user." - Name the service or object the tool acts on. "Create an issue in a GitHub repository" matches more tasks than "Create an item." @@ -182,10 +179,10 @@ That makes tool descriptions the main factor you control. A tool with a vague or <Cards> <Cards.Card title="Configure Tool Recommendation" - href="/operate/governance/mcp-gateways/tool-recommendation/configure" + href="/en/operate/governance/mcp-gateways/tool-recommendation/configure" /> <Cards.Card title="Tool Recommendation limitations" - href="/operate/governance/mcp-gateways/tool-recommendation/limitations" + href="/en/operate/governance/mcp-gateways/tool-recommendation/limitations" /> </Cards> diff --git a/app/en/operate/governance/mcp-gateways/tool-recommendation/limitations/page.mdx b/content/docs/operate/governance/mcp-gateways/tool-recommendation/limitations.mdx similarity index 57% rename from app/en/operate/governance/mcp-gateways/tool-recommendation/limitations/page.mdx rename to content/docs/operate/governance/mcp-gateways/tool-recommendation/limitations.mdx index b01dee084..53e40c3fa 100644 --- a/app/en/operate/governance/mcp-gateways/tool-recommendation/limitations/page.mdx +++ b/content/docs/operate/governance/mcp-gateways/tool-recommendation/limitations.mdx @@ -1,11 +1,10 @@ --- -title: "Tool Recommendation limitations" -description: "Cases where Tool Recommendation does not help, and the limits to plan around when you turn it on" +title: Tool Recommendation limitations +sidebarTitle: Limitations +description: Cases where Tool Recommendation does not help, and the limits to plan around when you turn it on --- -# Tool Recommendation limitations - -[Tool Recommendation](/operate/governance/mcp-gateways/tool-recommendation) helps when a gateway has more tools than an agent can reasonably hold in context. It is not the right choice for every gateway. This page is for platform operators and agent developers deciding whether to turn it on. It covers the cases where it costs more than it saves, and the limits to plan around when you do use it. +[Tool Recommendation](/en/operate/governance/mcp-gateways/tool-recommendation) helps when a gateway has more tools than an agent can reasonably hold in context. It is not the right choice for every gateway. This page is for platform operators and agent developers deciding whether to turn it on. It covers the cases where it costs more than it saves, and the limits to plan around when you do use it. ## When to turn it off @@ -19,23 +18,23 @@ For a small gateway, turn Tool Recommendation off and let the MCP client list ev Some agents run a fixed workflow using a small, known set of tools. If those tool definitions already fit comfortably in the model's context, discovering them through Tool Recommendation can add an unnecessary round trip. Providing the tools directly may be simpler and faster. -For those agents, either use a gateway with Tool Recommendation off, or connect that one agent with `?tool_recommendation=false` on the gateway URL so that its tool list contains every allowed tool. See [Opt out for one connection](/operate/governance/mcp-gateways/tool-recommendation/configure#opt-out-for-one-connection). +For those agents, either use a gateway with Tool Recommendation off, or connect that one agent with `?tool_recommendation=false` on the gateway URL so that its tool list contains every allowed tool. See [Opt out for one connection](/en/operate/governance/mcp-gateways/tool-recommendation/configure#opt-out-for-one-connection). ### Tools with thin descriptions Arcade recommends tools by matching a task against each tool's name and the first line of its description. A toolset with short, vague, or near-identical descriptions produces poor recommendations: the right tool ranks low, or a similar-sounding tool outranks it. -If a gateway depends on tools like that, improve the descriptions before you turn Tool Recommendation on. See [What makes recommendations good](/operate/governance/mcp-gateways/tool-recommendation#what-makes-recommendations-good). +If a gateway depends on tools like that, improve the descriptions before you turn Tool Recommendation on. See [What makes recommendations good](/en/operate/governance/mcp-gateways/tool-recommendation#what-makes-recommendations-good). ## Limits to plan around ### New custom and remote tools take a moment to become recommendable -Arcade indexes your own tools and tools from [remote MCP servers](/operate/governance/remote-mcp-servers) in the background. A tool of either kind that you just added can be missing from recommendations for a short time. +Arcade indexes your own tools and tools from [remote MCP servers](/en/operate/governance/remote-mcp-servers) in the background. A tool of either kind that you just added can be missing from recommendations for a short time. ### Each task returns a limited number of tools -`Arcade.SelectTools` returns up to five tools for each task. Callers of the `POST /v1/tools/select` endpoint in the [Arcade API](/references/api) can change that count with `top_k`, up to 20. A request that bundles more than one action into one task can miss tools that fit only part of it. Agents get better results when they pass each action as a separate entry in `tasks`. The description of the `tasks` parameter asks the model to do this, but an agent with its own tool-calling prompt may need the same instruction. +`Arcade.SelectTools` returns up to five tools for each task. Callers of the `POST /v1/tools/select` endpoint in the [Arcade API](/en/references/api) can change that count with `top_k`, up to 20. A request that bundles more than one action into one task can miss tools that fit only part of it. Agents get better results when they pass each action as a separate entry in `tasks`. The description of the `tasks` parameter asks the model to do this, but an agent with its own tool-calling prompt may need the same instruction. ### Recommendations only cover allowed tools @@ -43,5 +42,5 @@ Tool Recommendation searches the tools the gateway allows and nothing else. It d ## Next steps -- [Configure Tool Recommendation](/operate/governance/mcp-gateways/tool-recommendation/configure) for a gateway. -- Learn how to [create tools](/build/create-tools) with descriptions that recommend well. +- [Configure Tool Recommendation](/en/operate/governance/mcp-gateways/tool-recommendation/configure) for a gateway. +- Learn how to [create tools](/en/build/create-tools) with descriptions that recommend well. diff --git a/content/docs/operate/governance/mcp-gateways/tool-recommendation/meta.json b/content/docs/operate/governance/mcp-gateways/tool-recommendation/meta.json new file mode 100644 index 000000000..ba52b569a --- /dev/null +++ b/content/docs/operate/governance/mcp-gateways/tool-recommendation/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Tool Recommendation", + "pages": ["index", "configure", "limitations"] +} diff --git a/content/docs/operate/governance/meta.json b/content/docs/operate/governance/meta.json new file mode 100644 index 000000000..54f65e2d1 --- /dev/null +++ b/content/docs/operate/governance/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Governance", + "pages": [ + "index", + "mcp-gateways", + "remote-mcp-servers", + "private-registry", + "contextual-access", + "audit-logs", + "tool-executions" + ] +} diff --git a/app/en/operate/governance/private-registry/page.mdx b/content/docs/operate/governance/private-registry.mdx similarity index 93% rename from app/en/operate/governance/private-registry/page.mdx rename to content/docs/operate/governance/private-registry.mdx index 5838fb3fc..be1680f95 100644 --- a/app/en/operate/governance/private-registry/page.mdx +++ b/content/docs/operate/governance/private-registry.mdx @@ -1,13 +1,10 @@ --- -title: "Private Registry" -description: "Share a login-gated catalog of each MCP gateway with end users, and review the capability requests they send back" +title: Private Registry +description: Share a login-gated catalog of each MCP gateway with end users, and review the capability requests they send back --- -import { Callout, Steps } from "nextra/components"; import Image from "next/image"; -# Private Registry - This page is for platform operators who roll out Arcade MCP gateways to end users. Every gateway has a Private Registry: an authentication-gated page at the gateway URL where end users see the apps and tools the gateway gives them, interact with tool capabilities, and ask for what's missing. Requests from every gateway in a project land in one dashboard **Inbox**, ranked by how many people want them. We recommend using the Registry when your rollout grows past a pilot group and you can no longer walk each person through what their agent can do. ## How it works @@ -18,7 +15,7 @@ This page is for platform operators who roll out Arcade MCP gateways to end user 4. When something is missing, the end user (or their agent) files a capability request, or upvotes a request someone already filed. 5. You review requests in the dashboard **Inbox** and respond. End users on that gateway see your response. -The Registry only shows what the gateway already exposes. Filing or responding to a request never adds tools to a gateway or changes who can reach them. You enable new tools separately, by [editing the gateway](/operate/governance/mcp-gateways/create-via-dashboard). +The Registry only shows what the gateway already exposes. Filing or responding to a request never adds tools to a gateway or changes who can reach them. You enable new tools separately, by [editing the gateway](/en/operate/governance/mcp-gateways/create-via-dashboard). ## Share the Registry @@ -46,7 +43,7 @@ Open the actions menu (**⋮**) on the gateway row. Under **Employee registry**, ### Sign-in by gateway auth type -Every gateway has a Registry, whatever its [authentication mode](/operate/governance/mcp-gateways#authentication). The Registry detects which sign-in to show: +Every gateway has a Registry, whatever its [authentication mode](/en/operate/governance/mcp-gateways#authentication). The Registry detects which sign-in to show: | Gateway authentication | How end users sign in to the Registry | | --- | --- | @@ -212,7 +209,7 @@ Select **Respond**. The request moves out of your open inbox. End users on that To read a response later, turn on **Include responded** and select **Response** on the row. -Responding does not change the gateway. If you decide to add the capability, edit the gateway's tools or add a [remote MCP server](/operate/governance/remote-mcp-servers) as a separate step. Viewing the Inbox requires permission to read MCP gateways; responding requires permission to update them. +Responding does not change the gateway. If you decide to add the capability, edit the gateway's tools or add a [remote MCP server](/en/operate/governance/remote-mcp-servers) as a separate step. Viewing the Inbox requires permission to read MCP gateways; responding requires permission to update them. ## Use the API @@ -277,6 +274,6 @@ curl -s -X POST "https://api.arcade.dev/v1/orgs/{org_id}/projects/{project_id}/g ## Next steps -- [Create an MCP gateway](/operate/governance/mcp-gateways/create-via-dashboard) to share with end users -- [Set up a User Source](/operate/identity/user-sources) so Arcade attributes requests to verified identities -- [Add remote MCP servers](/operate/governance/remote-mcp-servers) to fill the gaps end users report +- [Create an MCP gateway](/en/operate/governance/mcp-gateways/create-via-dashboard) to share with end users +- [Set up a User Source](/en/operate/identity/user-sources) so Arcade attributes requests to verified identities +- [Add remote MCP servers](/en/operate/governance/remote-mcp-servers) to fill the gaps end users report diff --git a/app/en/operate/governance/remote-mcp-servers/atlassian/page.mdx b/content/docs/operate/governance/remote-mcp-servers/atlassian.mdx similarity index 90% rename from app/en/operate/governance/remote-mcp-servers/atlassian/page.mdx rename to content/docs/operate/governance/remote-mcp-servers/atlassian.mdx index b6833aeec..7a7a107cf 100644 --- a/app/en/operate/governance/remote-mcp-servers/atlassian/page.mdx +++ b/content/docs/operate/governance/remote-mcp-servers/atlassian.mdx @@ -1,20 +1,18 @@ --- -title: "Connect an Atlassian Remote MCP Server" -description: "Connect Arcade to Atlassian's Rovo MCP server and configure the site-level domain, permission, and IP controls that govern access" +title: Connect an Atlassian Remote MCP Server +sidebarTitle: Atlassian +description: Connect Arcade to Atlassian's Rovo MCP server and configure the site-level domain, permission, and IP controls that govern access --- -import { Callout, Steps } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; +import { SignupLink } from "@/components/analytics"; -# Connect an Atlassian Remote MCP Server - -Atlassian hosts a remote MCP server at `mcp.atlassian.com`, exposing Jira, Confluence, Jira Service Management, Bitbucket, and Compass tools from an Atlassian Cloud site. This guide covers the Arcade-side setup for connecting it as a [remote MCP server](/operate/governance/remote-mcp-servers), plus the Atlassian administrator controls that most commonly trip people up. +Atlassian hosts a remote MCP server at `mcp.atlassian.com`, exposing Jira, Confluence, Jira Service Management, Bitbucket, and Compass tools from an Atlassian Cloud site. This guide covers the Arcade-side setup for connecting it as a [remote MCP server](/en/operate/governance/remote-mcp-servers), plus the Atlassian administrator controls that most commonly trip people up. <Callout type="info"> This guide is about connecting to Atlassian's Rovo MCP server, not an Arcade toolkit. Arcade already ships Optimized - [Jira](/resources/integrations/productivity/jira) and - [Confluence](/resources/integrations/productivity/confluence) toolkits, + [Jira](/en/resources/integrations/productivity/jira) and + [Confluence](/en/resources/integrations/productivity/confluence) toolkits, with none of the setup below. Reach for this remote-server path instead when you need: @@ -115,5 +113,5 @@ Save the server to open the authorization prompt. If this is the first time anyo ## Next steps -- [Create an MCP Gateway](/operate/governance/mcp-gateways/create-via-dashboard) to expose this server's tools. -- [Connect to MCP clients](/get-started/mcp-clients). +- [Create an MCP Gateway](/en/operate/governance/mcp-gateways/create-via-dashboard) to expose this server's tools. +- [Connect to MCP clients](/en/get-started/mcp-clients). diff --git a/app/en/operate/governance/remote-mcp-servers/aws/page.mdx b/content/docs/operate/governance/remote-mcp-servers/aws.mdx similarity index 92% rename from app/en/operate/governance/remote-mcp-servers/aws/page.mdx rename to content/docs/operate/governance/remote-mcp-servers/aws.mdx index 0804c221d..5a1021046 100644 --- a/app/en/operate/governance/remote-mcp-servers/aws/page.mdx +++ b/content/docs/operate/governance/remote-mcp-servers/aws.mdx @@ -1,14 +1,12 @@ --- -title: "Connect the AWS MCP Server" -description: "Connect Arcade to the AWS-managed MCP Server using AWS Sign-In OAuth, with access governed entirely by IAM" +title: Connect the AWS MCP Server +sidebarTitle: AWS +description: Connect Arcade to the AWS-managed MCP Server using AWS Sign-In OAuth, with access governed entirely by IAM --- -import { Callout, Steps } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; +import { SignupLink } from "@/components/analytics"; -# Connect the AWS MCP Server - -AWS hosts a managed **AWS MCP Server** that gives agents access to AWS APIs across thousands of operations, authorized through **AWS Sign-In** using the same identities, permissions, and governance model you already use with the AWS Management Console and CLI. This guide covers the Arcade-side setup for connecting it as a [remote MCP server](/operate/governance/remote-mcp-servers), plus the AWS settings that most commonly trip people up. +AWS hosts a managed **AWS MCP Server** that gives agents access to AWS APIs across thousands of operations, authorized through **AWS Sign-In** using the same identities, permissions, and governance model you already use with the AWS Management Console and CLI. This guide covers the Arcade-side setup for connecting it as a [remote MCP server](/en/operate/governance/remote-mcp-servers), plus the AWS settings that most commonly trip people up. <Callout type="info"> This guide is about connecting to the AWS MCP Server, not an Arcade @@ -112,5 +110,5 @@ Save the server to open the authorization prompt. Sign in through AWS Sign-In as ## Next steps -- [Create an MCP Gateway](/operate/governance/mcp-gateways/create-via-dashboard) to expose this server's tools. -- [Connect to MCP clients](/get-started/mcp-clients). +- [Create an MCP Gateway](/en/operate/governance/mcp-gateways/create-via-dashboard) to expose this server's tools. +- [Connect to MCP clients](/en/get-started/mcp-clients). diff --git a/app/en/operate/governance/remote-mcp-servers/dynamics-365-customer-service/page.mdx b/content/docs/operate/governance/remote-mcp-servers/dynamics-365-customer-service.mdx similarity index 92% rename from app/en/operate/governance/remote-mcp-servers/dynamics-365-customer-service/page.mdx rename to content/docs/operate/governance/remote-mcp-servers/dynamics-365-customer-service.mdx index 5961e72ce..2de70c14b 100644 --- a/app/en/operate/governance/remote-mcp-servers/dynamics-365-customer-service/page.mdx +++ b/content/docs/operate/governance/remote-mcp-servers/dynamics-365-customer-service.mdx @@ -1,14 +1,12 @@ --- -title: "Connect a Dynamics 365 Customer Service MCP Server" -description: "Configure a Microsoft Entra app registration and Arcade's OAuth 2.0 settings to connect the Dynamics 365 CX MCP Server - Service through Agent 365 Tooling Gateway" +title: Connect a Dynamics 365 Customer Service MCP Server +sidebarTitle: Dynamics 365 Customer Service +description: Configure a Microsoft Entra app registration and Arcade's OAuth 2.0 settings to connect the Dynamics 365 CX MCP Server - Service through Agent 365 Tooling Gateway --- -import { Callout, Steps } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; +import { SignupLink } from "@/components/analytics"; -# Connect a Dynamics 365 Customer Service MCP Server - -Microsoft fronts the **Dynamics 365 CX MCP Server - Service** with **Agent 365 Tooling Gateway (ATG)**, a Microsoft-hosted gateway that handles authentication to Dataverse and exposes Customer Service tools (case management, knowledge search, activity timelines, and email drafting) over MCP. This guide covers the Arcade-side setup for connecting it as a [remote MCP server](/operate/governance/remote-mcp-servers), plus the Microsoft Entra settings that most commonly trip people up. +Microsoft fronts the **Dynamics 365 CX MCP Server - Service** with **Agent 365 Tooling Gateway (ATG)**, a Microsoft-hosted gateway that handles authentication to Dataverse and exposes Customer Service tools (case management, knowledge search, activity timelines, and email drafting) over MCP. This guide covers the Arcade-side setup for connecting it as a [remote MCP server](/en/operate/governance/remote-mcp-servers), plus the Microsoft Entra settings that most commonly trip people up. <Callout type="info"> This guide is about connecting to the Dynamics 365 CX MCP Server - @@ -132,5 +130,5 @@ Save the server to open the authorization prompt. Sign in with an account that h ## Next steps -- [Create an MCP Gateway](/operate/governance/mcp-gateways/create-via-dashboard) to expose this server's tools. -- [Connect to MCP clients](/get-started/mcp-clients). +- [Create an MCP Gateway](/en/operate/governance/mcp-gateways/create-via-dashboard) to expose this server's tools. +- [Connect to MCP clients](/en/get-started/mcp-clients). diff --git a/app/en/operate/governance/remote-mcp-servers/github/page.mdx b/content/docs/operate/governance/remote-mcp-servers/github.mdx similarity index 92% rename from app/en/operate/governance/remote-mcp-servers/github/page.mdx rename to content/docs/operate/governance/remote-mcp-servers/github.mdx index 4ddee984a..f5f468e5c 100644 --- a/app/en/operate/governance/remote-mcp-servers/github/page.mdx +++ b/content/docs/operate/governance/remote-mcp-servers/github.mdx @@ -1,20 +1,18 @@ --- -title: "Connect a GitHub Remote MCP Server" -description: "Connect Arcade to GitHub's hosted MCP server using a fine-grained PAT or a registered OAuth App / GitHub App" +title: Connect a GitHub Remote MCP Server +sidebarTitle: GitHub +description: Connect Arcade to GitHub's hosted MCP server using a fine-grained PAT or a registered OAuth App / GitHub App --- -import { Callout, Steps } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; +import { SignupLink } from "@/components/analytics"; -# Connect a GitHub Remote MCP Server - -GitHub hosts a remote MCP server at `api.githubcopilot.com/mcp/`, exposing repositories, issues, pull requests, Actions, code security alerts, Projects, and more through roughly 80 tools. This guide covers the Arcade-side setup for connecting it as a [remote MCP server](/operate/governance/remote-mcp-servers), plus the GitHub settings that most commonly trip people up. +GitHub hosts a remote MCP server at `api.githubcopilot.com/mcp/`, exposing repositories, issues, pull requests, Actions, code security alerts, Projects, and more through roughly 80 tools. This guide covers the Arcade-side setup for connecting it as a [remote MCP server](/en/operate/governance/remote-mcp-servers), plus the GitHub settings that most commonly trip people up. <Callout type="info"> This guide is about connecting to GitHub's own remote MCP server, not the Arcade GitHub toolkits. Arcade already covers most of this surface with two toolkits: the [Optimized GitHub - toolkit](/resources/integrations/development/github), and the `githubapi` + toolkit](/en/resources/integrations/development/github), and the `githubapi` starter toolkit (818 tools, including Actions logs, Dependabot alerts, and code scanning alerts). The one confirmed gap is Copilot Spaces, which neither toolkit covers. @@ -137,5 +135,5 @@ Save the server to open the authorization prompt. Sign in with a GitHub account ## Next steps -- [Create an MCP Gateway](/operate/governance/mcp-gateways/create-via-dashboard) to expose this server's tools. -- [Connect to MCP clients](/get-started/mcp-clients). +- [Create an MCP Gateway](/en/operate/governance/mcp-gateways/create-via-dashboard) to expose this server's tools. +- [Connect to MCP clients](/en/get-started/mcp-clients). diff --git a/app/en/operate/governance/remote-mcp-servers/hubspot/page.mdx b/content/docs/operate/governance/remote-mcp-servers/hubspot.mdx similarity index 91% rename from app/en/operate/governance/remote-mcp-servers/hubspot/page.mdx rename to content/docs/operate/governance/remote-mcp-servers/hubspot.mdx index 3798b42d0..1d4f02a0d 100644 --- a/app/en/operate/governance/remote-mcp-servers/hubspot/page.mdx +++ b/content/docs/operate/governance/remote-mcp-servers/hubspot.mdx @@ -1,20 +1,18 @@ --- -title: "Connect a HubSpot Remote MCP Server" -description: "Configure a HubSpot MCP Auth App and Arcade's OAuth 2.0 settings to connect the HubSpot Remote MCP Server" +title: Connect a HubSpot Remote MCP Server +sidebarTitle: HubSpot +description: Configure a HubSpot MCP Auth App and Arcade's OAuth 2.0 settings to connect the HubSpot Remote MCP Server --- -import { Callout, Steps } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; +import { SignupLink } from "@/components/analytics"; -# Connect a HubSpot Remote MCP Server - -HubSpot hosts a remote MCP server at `mcp.hubspot.com`, exposing CRM records, activities, conversations, campaigns, and marketing email tools directly from a HubSpot portal. This guide covers the Arcade-side setup for connecting it as a [remote MCP server](/operate/governance/remote-mcp-servers), plus the HubSpot settings that most commonly trip people up. +HubSpot hosts a remote MCP server at `mcp.hubspot.com`, exposing CRM records, activities, conversations, campaigns, and marketing email tools directly from a HubSpot portal. This guide covers the Arcade-side setup for connecting it as a [remote MCP server](/en/operate/governance/remote-mcp-servers), plus the HubSpot settings that most commonly trip people up. <Callout type="info"> This guide is about connecting to HubSpot's own remote MCP server, not the Arcade HubSpot toolkits. Arcade already covers most of this surface across two toolkit tiers: the [HubSpot - toolkit](/resources/integrations/sales/hubspot) for core CRM objects, and + toolkit](/en/resources/integrations/sales/hubspot) for core CRM objects, and starter toolkits like `hubspotconversationsapi` and `hubspotmarketingapi` for conversations, campaigns, and marketing email. @@ -112,5 +110,5 @@ Save the server to open the authorization prompt. During authorization you'll se ## Next steps -- [Create an MCP Gateway](/operate/governance/mcp-gateways/create-via-dashboard) to expose this server's tools. -- [Connect to MCP clients](/get-started/mcp-clients). +- [Create an MCP Gateway](/en/operate/governance/mcp-gateways/create-via-dashboard) to expose this server's tools. +- [Connect to MCP clients](/en/get-started/mcp-clients). diff --git a/app/en/operate/governance/remote-mcp-servers/page.mdx b/content/docs/operate/governance/remote-mcp-servers/index.mdx similarity index 87% rename from app/en/operate/governance/remote-mcp-servers/page.mdx rename to content/docs/operate/governance/remote-mcp-servers/index.mdx index 7987eefc3..bf232d63c 100644 --- a/app/en/operate/governance/remote-mcp-servers/page.mdx +++ b/content/docs/operate/governance/remote-mcp-servers/index.mdx @@ -1,11 +1,11 @@ --- -title: "Remote MCP servers" -description: "Register an MCP server that Arcade does not host, then govern its tools alongside Arcade's own" +title: Remote MCP servers +sidebarTitle: Overview +description: Register an MCP server that Arcade does not host, then govern its tools alongside Arcade's own --- import Image from "next/image"; -import { Callout, Steps, Tabs } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; +import { SignupLink } from "@/components/analytics"; export const REMOTE_SETUP_WIDTH = 557 / 1.5; export const REMOTE_SETUP_HEIGHT = 773 / 1.5; @@ -14,8 +14,6 @@ export const ADVANCED_SETTINGS_HEIGHT = 1958 / 2; export const SERVERS_LIST_WIDTH = 2799 / 2; export const SERVERS_LIST_HEIGHT = 1837 / 2; -# Remote MCP servers - A remote MCP server is an MCP server that Arcade does not host: one your team already runs, one that a vendor hosts for you, or one from an Arcade partner. You register it **once, and to a scope** — a single project, or your whole organization — not to a gateway. From that point its tools are part of that scope's tool catalog alongside Arcade's own: every gateway in scope can select from them, the Playground can call them, and the SDKs can execute them. Registering to a second gateway, or re-running setup per gateway, is never something you do. @@ -23,7 +21,7 @@ You register it **once, and to a scope** — a single project, or your whole org This guide covers registering the server, configuring its connection and auth, and using its tools. <Callout type="info"> - Check the [MCP server catalog](/resources/integrations) first. When Arcade + Check the [MCP server catalog](/en/resources/integrations) first. When Arcade already ships a server for a service, its tools are agent-optimized and maintained for you, which beats registering that vendor's remote server yourself. Reach for a remote MCP server when the catalog doesn't cover it. @@ -31,7 +29,7 @@ This guide covers registering the server, configuring its connection and auth, a <Callout type="info"> Looking to *write* new tools rather than connect an existing server? See [Build - an MCP server](/build/create-tools/tool-basics/build-mcp-server) instead. This + an MCP server](/en/build/create-tools/tool-basics/build-mcp-server) instead. This page is for servers that already exist and are reachable over HTTP. </Callout> @@ -113,7 +111,7 @@ Choose **Remote MCP** as the server type, then fill in the two required fields: - **ID**: A unique, human-readable identifier (for example, `render-mcp-server`) - **URI**: The public URL for your MCP server (for example, `https://mcp.render.com/mcp`) -Select **Arcade** instead of **Remote MCP** if you are registering a server that Arcade or your own infrastructure hosts. See [Hybrid MCP servers](/operate/deploy/on-prem) for that flow. +Select **Arcade** instead of **Remote MCP** if you are registering a server that Arcade or your own infrastructure hosts. See [Hybrid MCP servers](/en/operate/deploy/on-prem) for that flow. <Callout type="info"> Use the [MCP Debugger](https://mcpdebugger.dev) to verify the remote server's @@ -175,7 +173,7 @@ Common settings include: height={ADVANCED_SETTINGS_HEIGHT} /> -Some remote servers need provider-specific setup beyond these generic settings. See [Connect a Salesforce Hosted MCP Server](/operate/governance/remote-mcp-servers/salesforce), [Connect a ServiceNow Hosted MCP Server](/operate/governance/remote-mcp-servers/servicenow), [Connect a Dynamics 365 Customer Service MCP Server](/operate/governance/remote-mcp-servers/dynamics-365-customer-service), [Connect a HubSpot Remote MCP Server](/operate/governance/remote-mcp-servers/hubspot), [Connect a Snowflake-Managed MCP Server](/operate/governance/remote-mcp-servers/snowflake), [Connect an Atlassian Remote MCP Server](/operate/governance/remote-mcp-servers/atlassian), [Connect a GitHub Remote MCP Server](/operate/governance/remote-mcp-servers/github), [Connect a Splunk MCP Server](/operate/governance/remote-mcp-servers/splunk), or [Connect the AWS MCP Server](/operate/governance/remote-mcp-servers/aws) for fully worked examples. +Some remote servers need provider-specific setup beyond these generic settings. See [Connect a Salesforce Hosted MCP Server](/en/operate/governance/remote-mcp-servers/salesforce), [Connect a ServiceNow Hosted MCP Server](/en/operate/governance/remote-mcp-servers/servicenow), [Connect a Dynamics 365 Customer Service MCP Server](/en/operate/governance/remote-mcp-servers/dynamics-365-customer-service), [Connect a HubSpot Remote MCP Server](/en/operate/governance/remote-mcp-servers/hubspot), [Connect a Snowflake-Managed MCP Server](/en/operate/governance/remote-mcp-servers/snowflake), [Connect an Atlassian Remote MCP Server](/en/operate/governance/remote-mcp-servers/atlassian), [Connect a GitHub Remote MCP Server](/en/operate/governance/remote-mcp-servers/github), [Connect a Splunk MCP Server](/en/operate/governance/remote-mcp-servers/splunk), or [Connect the AWS MCP Server](/en/operate/governance/remote-mcp-servers/aws) for fully worked examples. ## Where the server's tools become available @@ -250,5 +248,5 @@ console.log(result); ## Next steps -- [Create an MCP Gateway](/operate/governance/mcp-gateways/create-via-dashboard) -- [Connect to MCP clients](/get-started/mcp-clients) +- [Create an MCP Gateway](/en/operate/governance/mcp-gateways/create-via-dashboard) +- [Connect to MCP clients](/en/get-started/mcp-clients) diff --git a/content/docs/operate/governance/remote-mcp-servers/meta.json b/content/docs/operate/governance/remote-mcp-servers/meta.json new file mode 100644 index 000000000..5490c72f6 --- /dev/null +++ b/content/docs/operate/governance/remote-mcp-servers/meta.json @@ -0,0 +1,15 @@ +{ + "title": "Remote MCP servers", + "pages": [ + "index", + "salesforce", + "servicenow", + "dynamics-365-customer-service", + "hubspot", + "snowflake", + "atlassian", + "github", + "splunk", + "aws" + ] +} diff --git a/app/en/operate/governance/remote-mcp-servers/salesforce/page.mdx b/content/docs/operate/governance/remote-mcp-servers/salesforce.mdx similarity index 90% rename from app/en/operate/governance/remote-mcp-servers/salesforce/page.mdx rename to content/docs/operate/governance/remote-mcp-servers/salesforce.mdx index 074d3aaa0..291e7e770 100644 --- a/app/en/operate/governance/remote-mcp-servers/salesforce/page.mdx +++ b/content/docs/operate/governance/remote-mcp-servers/salesforce.mdx @@ -1,19 +1,17 @@ --- -title: "Connect a Salesforce Hosted MCP Server" -description: "Configure a Salesforce External Client App and Arcade's OAuth 2.0 settings to connect a Salesforce Hosted MCP Server" +title: Connect a Salesforce Hosted MCP Server +sidebarTitle: Salesforce +description: Configure a Salesforce External Client App and Arcade's OAuth 2.0 settings to connect a Salesforce Hosted MCP Server --- -import { Callout, Steps } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; +import { SignupLink } from "@/components/analytics"; -# Connect a Salesforce Hosted MCP Server - -Salesforce can host an [MCP server](https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/hosted-mcp-servers-overview.html) for an org, exposing tools such as SOQL queries directly from Salesforce data. This guide covers the Arcade-side setup for connecting a Salesforce Hosted MCP Server as a [remote MCP server](/operate/governance/remote-mcp-servers), plus the handful of Salesforce settings that most commonly trip people up. +Salesforce can host an [MCP server](https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/hosted-mcp-servers-overview.html) for an org, exposing tools such as SOQL queries directly from Salesforce data. This guide covers the Arcade-side setup for connecting a Salesforce Hosted MCP Server as a [remote MCP server](/en/operate/governance/remote-mcp-servers), plus the handful of Salesforce settings that most commonly trip people up. <Callout type="info"> This guide is about connecting to a Salesforce Hosted MCP Server, not the Arcade Salesforce toolkit. Arcade's [Salesforce - toolkit](/resources/integrations/sales/salesforce) covers standard CRM + toolkit](/en/resources/integrations/sales/salesforce) covers standard CRM objects: accounts, contacts, leads, opportunities, and tasks. The Hosted MCP Server can front tools no generic toolkit could ever @@ -22,7 +20,7 @@ Salesforce can host an [MCP server](https://developer.salesforce.com/docs/platfo - Custom Apex classes, Agentforce actions, and Named Queries built for org-specific processes (a GC Cloud or TPM workflow, for example) - SOQL execution and Einstein Trust Layer access, beyond the toolkit's fixed CRM objects - Need something neither one covers? Build a custom tool with the [Arcade MCP SDK](/build/create-tools/tool-basics/build-mcp-server) against Salesforce's own APIs. + Need something neither one covers? Build a custom tool with the [Arcade MCP SDK](/en/build/create-tools/tool-basics/build-mcp-server) against Salesforce's own APIs. </Callout> <GuideOverview> @@ -123,5 +121,5 @@ Save the server to open the authorization prompt. Confirm `mcp_api` and `refresh ## Next steps -- [Create an MCP Gateway](/operate/governance/mcp-gateways/create-via-dashboard) to expose this server's tools. -- [Connect to MCP clients](/get-started/mcp-clients). +- [Create an MCP Gateway](/en/operate/governance/mcp-gateways/create-via-dashboard) to expose this server's tools. +- [Connect to MCP clients](/en/get-started/mcp-clients). diff --git a/app/en/operate/governance/remote-mcp-servers/servicenow/page.mdx b/content/docs/operate/governance/remote-mcp-servers/servicenow.mdx similarity index 90% rename from app/en/operate/governance/remote-mcp-servers/servicenow/page.mdx rename to content/docs/operate/governance/remote-mcp-servers/servicenow.mdx index eefa7a4cd..f8ee0777b 100644 --- a/app/en/operate/governance/remote-mcp-servers/servicenow/page.mdx +++ b/content/docs/operate/governance/remote-mcp-servers/servicenow.mdx @@ -1,14 +1,12 @@ --- -title: "Connect a ServiceNow Hosted MCP Server" -description: "Configure a ServiceNow inbound integration and Arcade's OAuth 2.0 settings to connect a ServiceNow MCP Server Console server" +title: Connect a ServiceNow Hosted MCP Server +sidebarTitle: ServiceNow +description: Configure a ServiceNow inbound integration and Arcade's OAuth 2.0 settings to connect a ServiceNow MCP Server Console server --- -import { Callout, Steps } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; +import { SignupLink } from "@/components/analytics"; -# Connect a ServiceNow Hosted MCP Server - -ServiceNow can host an [MCP server](https://www.servicenow.com/docs/r/intelligent-experiences/exploring-mcp-server-console.html) for an instance through **MCP Server Console**, exposing tools built on Now Assist Skills, Knowledge Graph, Subflows, Actions, and Scripted REST APIs directly from ServiceNow data. This guide covers the Arcade-side setup for connecting a ServiceNow MCP Server Console server as a [remote MCP server](/operate/governance/remote-mcp-servers), plus the handful of ServiceNow settings that most commonly trip people up. +ServiceNow can host an [MCP server](https://www.servicenow.com/docs/r/intelligent-experiences/exploring-mcp-server-console.html) for an instance through **MCP Server Console**, exposing tools built on Now Assist Skills, Knowledge Graph, Subflows, Actions, and Scripted REST APIs directly from ServiceNow data. This guide covers the Arcade-side setup for connecting a ServiceNow MCP Server Console server as a [remote MCP server](/en/operate/governance/remote-mcp-servers), plus the handful of ServiceNow settings that most commonly trip people up. <Callout type="warning"> MCP Server Console is a newer, actively evolving feature. Field names and @@ -19,12 +17,12 @@ ServiceNow can host an [MCP server](https://www.servicenow.com/docs/r/intelligen <Callout type="info"> This guide is about connecting to a ServiceNow-hosted MCP server. Arcade - also ships a [ServiceNow toolkit](/resources/integrations/customer-support/servicenow) + also ships a [ServiceNow toolkit](/en/resources/integrations/customer-support/servicenow) with pre-built tools for incidents, changes, catalog requests, knowledge articles, and the CMDB. That toolkit calls ServiceNow's REST APIs directly, so it needs neither MCP Server Console nor a Now Assist entitlement. Setting it up is covered in the - [ServiceNow auth provider reference](/references/auth-providers/servicenow). + [ServiceNow auth provider reference](/en/references/auth-providers/servicenow). Use MCP Server Console for what no generic toolkit could pre-build, since a customer defines these for their own instance: @@ -33,7 +31,7 @@ ServiceNow can host an [MCP server](https://www.servicenow.com/docs/r/intelligen - Actions and Scripted REST APIs scoped to that company's specific ITSM, HR, or business-unit configuration Need something neither one exposes? Build a custom - tool with the [Arcade MCP SDK](/build/create-tools/tool-basics/build-mcp-server) + tool with the [Arcade MCP SDK](/en/build/create-tools/tool-basics/build-mcp-server) against ServiceNow's Table API. </Callout> @@ -136,5 +134,5 @@ Save the server to open the authorization prompt. Sign in as a ServiceNow user w ## Next steps -- [Create an MCP Gateway](/operate/governance/mcp-gateways/create-via-dashboard) to expose this server's tools. -- [Connect to MCP clients](/get-started/mcp-clients). +- [Create an MCP Gateway](/en/operate/governance/mcp-gateways/create-via-dashboard) to expose this server's tools. +- [Connect to MCP clients](/en/get-started/mcp-clients). diff --git a/app/en/operate/governance/remote-mcp-servers/snowflake/page.mdx b/content/docs/operate/governance/remote-mcp-servers/snowflake.mdx similarity index 93% rename from app/en/operate/governance/remote-mcp-servers/snowflake/page.mdx rename to content/docs/operate/governance/remote-mcp-servers/snowflake.mdx index 85acc4505..5bea08e4a 100644 --- a/app/en/operate/governance/remote-mcp-servers/snowflake/page.mdx +++ b/content/docs/operate/governance/remote-mcp-servers/snowflake.mdx @@ -1,19 +1,17 @@ --- -title: "Connect a Snowflake-Managed MCP Server" -description: "Configure a Snowflake OAuth security integration and Arcade's OAuth 2.0 settings to connect a Snowflake-managed MCP server" +title: Connect a Snowflake-Managed MCP Server +sidebarTitle: Snowflake +description: Configure a Snowflake OAuth security integration and Arcade's OAuth 2.0 settings to connect a Snowflake-managed MCP server --- -import { Callout, Steps } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; +import { SignupLink } from "@/components/analytics"; -# Connect a Snowflake-Managed MCP Server - -Snowflake can host an MCP server object inside a database and schema, exposing Cortex Analyst, Cortex Search, Cortex Agents, SQL execution, and custom UDF/stored procedure tools without any separate infrastructure to deploy. This guide covers the Arcade-side setup for connecting a Snowflake-managed MCP server as a [remote MCP server](/operate/governance/remote-mcp-servers), plus the Snowflake settings that most commonly trip people up. +Snowflake can host an MCP server object inside a database and schema, exposing Cortex Analyst, Cortex Search, Cortex Agents, SQL execution, and custom UDF/stored procedure tools without any separate infrastructure to deploy. This guide covers the Arcade-side setup for connecting a Snowflake-managed MCP server as a [remote MCP server](/en/operate/governance/remote-mcp-servers), plus the Snowflake settings that most commonly trip people up. <Callout type="info"> This guide is about connecting to a Snowflake-managed MCP server, not the Arcade Snowflake toolkit. Arcade's [Snowflake - toolkit](/resources/integrations/databases/snowflake) covers query and + toolkit](/en/resources/integrations/databases/snowflake) covers query and write access, with none of the setup below. The managed MCP server can front tools no generic toolkit could ever @@ -174,5 +172,5 @@ This list mirrors Snowflake's own troubleshooting guidance, applied to an Arcade ## Next steps -- [Create an MCP Gateway](/operate/governance/mcp-gateways/create-via-dashboard) to expose this server's tools. -- [Connect to MCP clients](/get-started/mcp-clients). +- [Create an MCP Gateway](/en/operate/governance/mcp-gateways/create-via-dashboard) to expose this server's tools. +- [Connect to MCP clients](/en/get-started/mcp-clients). diff --git a/app/en/operate/governance/remote-mcp-servers/splunk/page.mdx b/content/docs/operate/governance/remote-mcp-servers/splunk.mdx similarity index 94% rename from app/en/operate/governance/remote-mcp-servers/splunk/page.mdx rename to content/docs/operate/governance/remote-mcp-servers/splunk.mdx index 22ff6fe00..a8cef2545 100644 --- a/app/en/operate/governance/remote-mcp-servers/splunk/page.mdx +++ b/content/docs/operate/governance/remote-mcp-servers/splunk.mdx @@ -1,14 +1,12 @@ --- -title: "Connect a Splunk MCP Server" -description: "Connect Arcade to the MCP Server for Splunk platform using a bearer token or OAuth 2.1" +title: Connect a Splunk MCP Server +sidebarTitle: Splunk +description: Connect Arcade to the MCP Server for Splunk platform using a bearer token or OAuth 2.1 --- -import { Callout, Steps } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; +import { SignupLink } from "@/components/analytics"; -# Connect a Splunk MCP Server - -Splunk hosts an MCP server through the **MCP Server for Splunk platform** app, installed on a Search Head or Search Head Cluster, exposing SPL search execution, saved searches, SPL generation/explanation/optimization, and customer-authored custom tools directly from a Splunk Cloud deployment. This guide covers the Arcade-side setup for connecting it as a [remote MCP server](/operate/governance/remote-mcp-servers), plus the Splunk settings that most commonly trip people up. +Splunk hosts an MCP server through the **MCP Server for Splunk platform** app, installed on a Search Head or Search Head Cluster, exposing SPL search execution, saved searches, SPL generation/explanation/optimization, and customer-authored custom tools directly from a Splunk Cloud deployment. This guide covers the Arcade-side setup for connecting it as a [remote MCP server](/en/operate/governance/remote-mcp-servers), plus the Splunk settings that most commonly trip people up. <Callout type="info"> This guide is about connecting to a Splunk MCP server. Arcade doesn't ship @@ -129,7 +127,7 @@ Copy the redirect URI Arcade generates and set it as the **Redirect URI** on the ### Authorize and confirm -Save the server. For the bearer-token path, click **Tools** to confirm Arcade can reach the server and discover its tools (roughly 15 come from a standard install). For the OAuth path, saving opens an authorization prompt. Sign in as a Splunk user whose role carries the `mcp_tool_execute` capability, since the session runs under that identity and its role determines which tools are reachable. Either way, a successful connection immediately lists the discovered tools, which you can then add to an [MCP Gateway](/operate/governance/mcp-gateways/create-via-dashboard). +Save the server. For the bearer-token path, click **Tools** to confirm Arcade can reach the server and discover its tools (roughly 15 come from a standard install). For the OAuth path, saving opens an authorization prompt. Sign in as a Splunk user whose role carries the `mcp_tool_execute` capability, since the session runs under that identity and its role determines which tools are reachable. Either way, a successful connection immediately lists the discovered tools, which you can then add to an [MCP Gateway](/en/operate/governance/mcp-gateways/create-via-dashboard). </Steps> @@ -148,5 +146,5 @@ The first two are the failures seen most often in practice, and they apply to ** ## Next steps -- [Create an MCP Gateway](/operate/governance/mcp-gateways/create-via-dashboard) to expose this server's tools. -- [Connect to MCP clients](/get-started/mcp-clients). +- [Create an MCP Gateway](/en/operate/governance/mcp-gateways/create-via-dashboard) to expose this server's tools. +- [Connect to MCP clients](/en/get-started/mcp-clients). diff --git a/app/en/operate/governance/tool-executions/page.mdx b/content/docs/operate/governance/tool-executions.mdx similarity index 94% rename from app/en/operate/governance/tool-executions/page.mdx rename to content/docs/operate/governance/tool-executions.mdx index 2443bc6d0..10cefda9f 100644 --- a/app/en/operate/governance/tool-executions/page.mdx +++ b/content/docs/operate/governance/tool-executions.mdx @@ -1,10 +1,10 @@ --- -title: "Tool Executions" +title: Tool executions +metaTitle: Tool Executions +sidebarTitle: Tool Executions description: "Review every tool run in a project: what ran, for whom, and why it failed, with tool inputs and outputs held to project admins." --- -# Tool executions - Arcade records every tool run in a project, including which tool ran, for which end user, when, and how it went, and exposes that history in the dashboard and over the API. Recording is on by default and is an organization-wide setting — see [Recording and retention](#recording-and-retention). This page serves two jobs: @@ -12,7 +12,7 @@ This page serves two jobs: - **Agent and tool developers** debug a run that misbehaved. The exact inputs a tool received and the outputs it returned show whether your agent sent the wrong arguments or the tool or its provider failed. - **Platform operators** review what a project has run, for compliance and to see which tools and toolkits people actually use. -Tool executions cover runtime activity. For administrative changes such as creating an API key or adding a project member, see [Audit Logs](/operate/governance/audit-logs). +Tool executions cover runtime activity. For administrative changes such as creating an API key or adding a project member, see [Audit Logs](/en/operate/governance/audit-logs). ## What Arcade records @@ -164,7 +164,7 @@ Two behaviors trip up API callers: - **The payload flags change the authority a request needs.** Omit `include_inputs` and `include_outputs` and the request needs only a role on the project. Add either one and the request needs project-admin authority, so the same caller can receive `200` without the flags and `403` with them. - **These endpoints serve operators and project API keys.** Arcade refuses a read-only project API key, and refuses any caller whose authority comes from a single end user, such as an MCP client. -Find your organization and project IDs in the dashboard URL: `/orgs/{org_id}/projects/{project_id}`. For the full request and response schemas, see the [API reference](/references/api). +Find your organization and project IDs in the dashboard URL: `/orgs/{org_id}/projects/{project_id}`. For the full request and response schemas, see the [API reference](/en/references/api). ## Recording and retention @@ -179,7 +179,7 @@ On Arcade Cloud, recording is enabled by default and the retention window is 7 d ### Change them yourself -In the dashboard, open **Organization → Logging Policy**. Or call the API. The policy belongs to the organization, so this endpoint authenticates a user the same way the [audit log API](/operate/governance/audit-logs) does — a project API key is refused with a `401`: +In the dashboard, open **Organization → Logging Policy**. Or call the API. The policy belongs to the organization, so this endpoint authenticates a user the same way the [audit log API](/en/operate/governance/audit-logs) does — a project API key is refused with a `401`: ```bash curl -s -X PUT "https://cloud.arcade.dev/api/v1/orgs/{org_id}/logging-config" \ @@ -199,6 +199,6 @@ A window must be at least 1 day and no longer than the platform maximum, which i ## Related content -- [Audit Logs](/operate/governance/audit-logs): administrative actions across the platform -- [Contextual Access](/operate/governance/contextual-access): policies that decide whether a tool call runs at all -- [API reference](/references/api): full schemas for every endpoint +- [Audit Logs](/en/operate/governance/audit-logs): administrative actions across the platform +- [Contextual Access](/en/operate/governance/contextual-access): policies that decide whether a tool call runs at all +- [API reference](/en/references/api): full schemas for every endpoint diff --git a/content/docs/operate/identity/index.mdx b/content/docs/operate/identity/index.mdx new file mode 100644 index 000000000..eb28ad44e --- /dev/null +++ b/content/docs/operate/identity/index.mdx @@ -0,0 +1,16 @@ +--- +title: Identity +sidebarTitle: Overview +description: Connect end-user identity providers and configure how gateways authenticate users +--- + +Configure how end users authenticate to Arcade gateways and how your organization manages auth providers. + +<Cards num={2}> + <Cards.Card title="User Sources" href="/en/operate/identity/user-sources" /> + <Cards.Card title="Auth providers" href="/en/references/auth-providers" /> + <Cards.Card + title="Secure auth in production" + href="/en/build/user-facing-agents/secure-auth-production" + /> +</Cards> diff --git a/content/docs/operate/identity/meta.json b/content/docs/operate/identity/meta.json new file mode 100644 index 000000000..ac0bfbc31 --- /dev/null +++ b/content/docs/operate/identity/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Identity", + "pages": ["index", "user-sources"] +} diff --git a/app/en/operate/identity/user-sources/auth0/page.mdx b/content/docs/operate/identity/user-sources/auth0.mdx similarity index 86% rename from app/en/operate/identity/user-sources/auth0/page.mdx rename to content/docs/operate/identity/user-sources/auth0.mdx index 23ec1e475..16b6f7241 100644 --- a/app/en/operate/identity/user-sources/auth0/page.mdx +++ b/content/docs/operate/identity/user-sources/auth0.mdx @@ -1,18 +1,15 @@ --- -title: "Auth0" -description: "Use Auth0 as a User Source so people who already sign in with their Auth0 account can sign in to your MCP Gateways" +title: Auth0 +description: Use Auth0 as a User Source so people who already sign in with their Auth0 account can sign in to your MCP Gateways --- import Image from "next/image"; -import { Steps } from "nextra/components"; export const IMAGE_SCALE_FACTOR = 2; export const AUTH0_SCREENSHOT_WIDTH = 1552 / IMAGE_SCALE_FACTOR; export const AUTH0_SCREENSHOT_HEIGHT = 1099 / IMAGE_SCALE_FACTOR; -# Auth0 - -Use Auth0 as a User Source so the people who already sign in through your Auth0 tenant can sign in to your MCP Gateways. This guide walks you through what to do in the Auth0 dashboard. Once you have the values it tells you to copy, [create the User Source in Arcade](/operate/identity/user-sources#create-a-user-source). +Use Auth0 as a User Source so the people who already sign in through your Auth0 tenant can sign in to your MCP Gateways. This guide walks you through what to do in the Auth0 dashboard. Once you have the values it tells you to copy, [create the User Source in Arcade](/en/operate/identity/user-sources#create-a-user-source). ## Before you start @@ -83,14 +80,14 @@ Copy these values into Arcade's User Source form: ## Create the User Source in Arcade -Open the Arcade Dashboard, select your project, navigate to **User Sources**, and click **Create User Source**. Paste the values from the table above. For a walkthrough of the form, see [Create a User Source](/operate/identity/user-sources#create-a-user-source) in the User Sources overview. +Open the Arcade Dashboard, select your project, navigate to **User Sources**, and click **Create User Source**. Paste the values from the table above. For a walkthrough of the form, see [Create a User Source](/en/operate/identity/user-sources#create-a-user-source) in the User Sources overview. -Once the User Source is active, attach it to an MCP Gateway by following [Create via Dashboard](/operate/governance/mcp-gateways/create-via-dashboard) and picking **User Source** under "Non-Arcade Users" in the gateway form. +Once the User Source is active, attach it to an MCP Gateway by following [Create via Dashboard](/en/operate/governance/mcp-gateways/create-via-dashboard) and picking **User Source** under "Non-Arcade Users" in the gateway form. ## Notes - **Tenant domains and regions**: Newer Auth0 tenants use region-prefixed domains such as `<tenant>.us.auth0.com` or `<tenant>.eu.auth0.com`. Older tenants use `<tenant>.auth0.com`. Either form works; copy exactly what the **Domain** field on the application's Settings tab displays. See Auth0's [tenant region documentation](https://auth0.com/docs/get-started/auth0-overview/create-tenants) for more on which region your tenant is in. - **Custom domains**: If you use Auth0's [custom domain feature](https://auth0.com/docs/customize/custom-domains) (for example, `auth.yourcompany.com`), use the custom domain as the **Issuer URL** hostname. End users see your branded domain on the sign-in page. - **Connections**: Auth0 lets you wire up database, social, and enterprise connections behind a single tenant. Arcade doesn't care which connections you've enabled, only that the end users who try to sign in have access to this application. Configure connections under **Authentication** → **Database / Social / Enterprise** in the Auth0 dashboard. -- **Client secret rotation**: Auth0 lets you rotate the client secret from the **Settings** tab's **Basic Information** section. See [Rotate the client secret](/operate/identity/user-sources#rotate-the-client-secret) in the User Sources guide for the Arcade side. -- **Custom subject claims**: Arcade's default Subject Claim, `sub`, is Auth0's connection-prefixed user identifier (for example, `auth0|abc123` for a database user or `google-oauth2|abc123` for a Google-linked user). It's stable per user across the tenant. If you need Arcade to identify users by a different value, add a [custom claim](https://auth0.com/docs/secure/tokens/json-web-tokens/create-custom-claims) to the ID token via an Auth0 Action and set the User Source's **Subject Claim** to its name. Arcade only reads ID tokens, so the claim must be on the ID token (not the access token). See [Advanced: scopes and subject claim](/operate/identity/user-sources#advanced-scopes-and-subject-claim) in the overview for what makes a good claim. +- **Client secret rotation**: Auth0 lets you rotate the client secret from the **Settings** tab's **Basic Information** section. See [Rotate the client secret](/en/operate/identity/user-sources#rotate-the-client-secret) in the User Sources guide for the Arcade side. +- **Custom subject claims**: Arcade's default Subject Claim, `sub`, is Auth0's connection-prefixed user identifier (for example, `auth0|abc123` for a database user or `google-oauth2|abc123` for a Google-linked user). It's stable per user across the tenant. If you need Arcade to identify users by a different value, add a [custom claim](https://auth0.com/docs/secure/tokens/json-web-tokens/create-custom-claims) to the ID token via an Auth0 Action and set the User Source's **Subject Claim** to its name. Arcade only reads ID tokens, so the claim must be on the ID token (not the access token). See [Advanced: scopes and subject claim](/en/operate/identity/user-sources#advanced-scopes-and-subject-claim) in the overview for what makes a good claim. diff --git a/app/en/operate/identity/user-sources/clerk/page.mdx b/content/docs/operate/identity/user-sources/clerk.mdx similarity index 86% rename from app/en/operate/identity/user-sources/clerk/page.mdx rename to content/docs/operate/identity/user-sources/clerk.mdx index f40fe869d..b8c1d1795 100644 --- a/app/en/operate/identity/user-sources/clerk/page.mdx +++ b/content/docs/operate/identity/user-sources/clerk.mdx @@ -1,18 +1,15 @@ --- -title: "Clerk" -description: "Use Clerk as a User Source so people who already sign in through your Clerk instance can sign in to your MCP Gateways" +title: Clerk +description: Use Clerk as a User Source so people who already sign in through your Clerk instance can sign in to your MCP Gateways --- import Image from "next/image"; -import { Steps } from "nextra/components"; export const IMAGE_SCALE_FACTOR = 1.5; export const CLERK_SCREENSHOT_WIDTH = 1312 / IMAGE_SCALE_FACTOR; export const CLERK_SCREENSHOT_HEIGHT = 949 / IMAGE_SCALE_FACTOR; -# Clerk - -Use Clerk as a User Source so the people who already sign in through your Clerk instance can sign in to your MCP Gateways. Clerk's [OAuth applications](https://clerk.com/docs/advanced-usage/clerk-idp) feature turns your Clerk instance into an OIDC identity provider that Arcade can connect to. Once you have the values it tells you to copy, [create the User Source in Arcade](/operate/identity/user-sources#create-a-user-source). +Use Clerk as a User Source so the people who already sign in through your Clerk instance can sign in to your MCP Gateways. Clerk's [OAuth applications](https://clerk.com/docs/advanced-usage/clerk-idp) feature turns your Clerk instance into an OIDC identity provider that Arcade can connect to. Once you have the values it tells you to copy, [create the User Source in Arcade](/en/operate/identity/user-sources#create-a-user-source). ## Before you start @@ -81,12 +78,12 @@ Copy these values into Arcade's User Source form: ## Create the User Source in Arcade -Open the Arcade Dashboard, select your project, navigate to **User Sources**, and click **Create User Source**. Paste the values from the table above. For a walkthrough of the form, see [Create a User Source](/operate/identity/user-sources#create-a-user-source) in the User Sources overview. +Open the Arcade Dashboard, select your project, navigate to **User Sources**, and click **Create User Source**. Paste the values from the table above. For a walkthrough of the form, see [Create a User Source](/en/operate/identity/user-sources#create-a-user-source) in the User Sources overview. -Once the User Source is active, attach it to an MCP Gateway by following [Create via Dashboard](/operate/governance/mcp-gateways/create-via-dashboard) and picking **User Source** under "Non-Arcade Users" in the gateway form. +Once the User Source is active, attach it to an MCP Gateway by following [Create via Dashboard](/en/operate/governance/mcp-gateways/create-via-dashboard) and picking **User Source** under "Non-Arcade Users" in the gateway form. ## Notes - **Development vs production instances**: Clerk's development and production instances have different Frontend API URLs and different OAuth applications. When you're ready to move from a development setup to production, register a new OAuth application in the production instance and create a separate Arcade User Source for it. -- **Client secret rotation**: Clerk supports regenerating the Client Secret from the OAuth application's **Application credentials** section. See [Rotate the client secret](/operate/identity/user-sources#rotate-the-client-secret) in the User Sources guide for the Arcade-side procedure. -- **Subject Claim**: Arcade's default Subject Claim, `sub`, is the Clerk user ID and is the recommended value. Clerk's OAuth applications also expose `public_metadata` and `private_metadata` scopes that surface the corresponding [user metadata](https://clerk.com/docs/users/metadata) on the ID token, which lets you add organization, role, or other custom data to claims. See [Advanced: scopes and subject claim](/operate/identity/user-sources#advanced-scopes-and-subject-claim) in the overview for what makes a good claim. +- **Client secret rotation**: Clerk supports regenerating the Client Secret from the OAuth application's **Application credentials** section. See [Rotate the client secret](/en/operate/identity/user-sources#rotate-the-client-secret) in the User Sources guide for the Arcade-side procedure. +- **Subject Claim**: Arcade's default Subject Claim, `sub`, is the Clerk user ID and is the recommended value. Clerk's OAuth applications also expose `public_metadata` and `private_metadata` scopes that surface the corresponding [user metadata](https://clerk.com/docs/users/metadata) on the ID token, which lets you add organization, role, or other custom data to claims. See [Advanced: scopes and subject claim](/en/operate/identity/user-sources#advanced-scopes-and-subject-claim) in the overview for what makes a good claim. diff --git a/app/en/operate/identity/user-sources/page.mdx b/content/docs/operate/identity/user-sources/index.mdx similarity index 91% rename from app/en/operate/identity/user-sources/page.mdx rename to content/docs/operate/identity/user-sources/index.mdx index 909514fea..d7353127a 100644 --- a/app/en/operate/identity/user-sources/page.mdx +++ b/content/docs/operate/identity/user-sources/index.mdx @@ -1,12 +1,9 @@ --- -title: "User Sources" -description: "Connect an OIDC identity provider to an MCP Gateway so your end users sign in with their existing identities" +title: User Sources +sidebarTitle: Overview +description: Connect an OIDC identity provider to an MCP Gateway so your end users sign in with their existing identities --- -import { Callout, Steps } from "nextra/components"; - -# User Sources - A User Source is the connection between an MCP Gateway and your OIDC identity provider. When a gateway uses a User Source, Arcade redirects each end user to your identity provider to sign in and then identifies them by an OIDC subject claim on every gateway request. ## What is a User Source? @@ -27,7 +24,7 @@ Arcade MCP Gateways support three ways to authenticate end users: | **User Source** (recommended for production) | Production agents whose end users have identities in your OIDC identity provider | | **Arcade Headers** (fallback) | MCP clients that can't run a browser-based OAuth flow | -See [MCP Gateway authentication](/operate/governance/mcp-gateways) for the full comparison and for how to pick an authentication mode when creating a gateway. +See [MCP Gateway authentication](/en/operate/governance/mcp-gateways) for the full comparison and for how to pick an authentication mode when creating a gateway. ## Register an OAuth client at your identity provider @@ -96,7 +93,7 @@ The **Subject Claim** is the JWT claim Arcade uses to identify each of your end You attach a User Source to an MCP Gateway when you create or edit the gateway. One User Source can back multiple gateways in the same project, so you can reuse the same end-user identity system across every gateway you build for those users. -See [Create via Dashboard](/operate/governance/mcp-gateways/create-via-dashboard) for how to pick a User Source when configuring a gateway's authentication. +See [Create via Dashboard](/en/operate/governance/mcp-gateways/create-via-dashboard) for how to pick a User Source when configuring a gateway's authentication. ## Manage User Sources @@ -130,5 +127,5 @@ Deleting a User Source is permanent. As with deactivation, you can only delete a ## Next steps -- [MCP Gateway authentication](/operate/governance/mcp-gateways) for a comparison of Arcade Auth, User Sources, and Arcade Headers -- [Connect to MCP clients](/get-started/mcp-clients) to point your client at a gateway that uses a User Source +- [MCP Gateway authentication](/en/operate/governance/mcp-gateways) for a comparison of Arcade Auth, User Sources, and Arcade Headers +- [Connect to MCP clients](/en/get-started/mcp-clients) to point your client at a gateway that uses a User Source diff --git a/content/docs/operate/identity/user-sources/meta.json b/content/docs/operate/identity/user-sources/meta.json new file mode 100644 index 000000000..7acd675dd --- /dev/null +++ b/content/docs/operate/identity/user-sources/meta.json @@ -0,0 +1,4 @@ +{ + "title": "User Sources", + "pages": ["index", "auth0", "clerk", "microsoft-entra-id", "okta", "stytch"] +} diff --git a/app/en/operate/identity/user-sources/microsoft-entra-id/page.mdx b/content/docs/operate/identity/user-sources/microsoft-entra-id.mdx similarity index 89% rename from app/en/operate/identity/user-sources/microsoft-entra-id/page.mdx rename to content/docs/operate/identity/user-sources/microsoft-entra-id.mdx index d2bb9dff0..ee69dfb67 100644 --- a/app/en/operate/identity/user-sources/microsoft-entra-id/page.mdx +++ b/content/docs/operate/identity/user-sources/microsoft-entra-id.mdx @@ -1,18 +1,15 @@ --- -title: "Microsoft Entra ID" -description: "Use Microsoft Entra ID as a User Source so people who already sign in with Microsoft work or school accounts can sign in to your MCP Gateways" +title: Microsoft Entra ID +description: Use Microsoft Entra ID as a User Source so people who already sign in with Microsoft work or school accounts can sign in to your MCP Gateways --- import Image from "next/image"; -import { Callout, Steps } from "nextra/components"; export const IMAGE_SCALE_FACTOR = 1.5; export const ENTRA_SCREENSHOT_WIDTH = 1312 / IMAGE_SCALE_FACTOR; export const ENTRA_SCREENSHOT_HEIGHT = 849 / IMAGE_SCALE_FACTOR; -# Microsoft Entra ID - -Use Microsoft Entra ID as a User Source so the people who already sign in with their Microsoft work or school account can sign in to your MCP Gateways. This guide walks you through what to do in the Microsoft Entra admin center. Once you have the values it tells you to copy, [create the User Source in Arcade](/operate/identity/user-sources#create-a-user-source). +Use Microsoft Entra ID as a User Source so the people who already sign in with their Microsoft work or school account can sign in to your MCP Gateways. This guide walks you through what to do in the Microsoft Entra admin center. Once you have the values it tells you to copy, [create the User Source in Arcade](/en/operate/identity/user-sources#create-a-user-source). ## Before you start @@ -111,9 +108,9 @@ Leave Arcade's other defaults alone for a standard Entra setup. See [Optional: u ## Create the User Source in Arcade -Open the Arcade Dashboard, select your project, navigate to **User Sources**, and click **Create User Source**. Paste the values from the table above. For a walkthrough of the form, see [Create a User Source](/operate/identity/user-sources#create-a-user-source) in the User Sources overview. +Open the Arcade Dashboard, select your project, navigate to **User Sources**, and click **Create User Source**. Paste the values from the table above. For a walkthrough of the form, see [Create a User Source](/en/operate/identity/user-sources#create-a-user-source) in the User Sources overview. -Once the User Source is active, attach it to an MCP Gateway by following [Create via Dashboard](/operate/governance/mcp-gateways/create-via-dashboard) and picking **User Source** under "Non-Arcade Users" in the gateway form. +Once the User Source is active, attach it to an MCP Gateway by following [Create via Dashboard](/en/operate/governance/mcp-gateways/create-via-dashboard) and picking **User Source** under "Non-Arcade Users" in the gateway form. ## Optional: use `oid` as the subject claim @@ -127,5 +124,5 @@ Entra only emits `oid` when `profile` is in the requested scopes. You granted th - **Multi-tenant and personal-account options aren't supported today**: Arcade matches the OIDC `iss` claim on every ID token byte-for-byte against the issuer URL you save on the User Source. The **Multiple Entra ID tenants**, **Any Entra ID Tenant + Personal Microsoft accounts**, and **Personal accounts only** options all rely on Entra issuing tokens whose `iss` reflects the signing-in user's home tenant, so there's no single issuer URL that will validate for users from more than one tenant. If you need to onboard users from multiple Entra tenants today, create a separate Arcade User Source per tenant. - **PKCE**: Arcade always uses PKCE when authenticating end users. Entra ID accepts PKCE automatically on the **Web** platform, so you don't need to change any Entra setting. -- **Client secret rotation**: Entra client secrets expire. Rotate yours before the expiration date. See [Rotate the client secret](/operate/identity/user-sources#rotate-the-client-secret) in the User Sources guide for how. -- **Custom subject claims**: If neither `sub` nor `oid` fits, you can configure a different value as an [optional claim](https://learn.microsoft.com/en-us/entra/identity-platform/optional-claims#configure-optional-claims-in-your-application) on Entra's ID token, then set the User Source's **Subject Claim** to its name. Arcade only reads ID tokens, so the claim must be on the ID token specifically (not the access token). See [Advanced: scopes and subject claim](/operate/identity/user-sources#advanced-scopes-and-subject-claim) in the overview for what makes a good claim. +- **Client secret rotation**: Entra client secrets expire. Rotate yours before the expiration date. See [Rotate the client secret](/en/operate/identity/user-sources#rotate-the-client-secret) in the User Sources guide for how. +- **Custom subject claims**: If neither `sub` nor `oid` fits, you can configure a different value as an [optional claim](https://learn.microsoft.com/en-us/entra/identity-platform/optional-claims#configure-optional-claims-in-your-application) on Entra's ID token, then set the User Source's **Subject Claim** to its name. Arcade only reads ID tokens, so the claim must be on the ID token specifically (not the access token). See [Advanced: scopes and subject claim](/en/operate/identity/user-sources#advanced-scopes-and-subject-claim) in the overview for what makes a good claim. diff --git a/app/en/operate/identity/user-sources/okta/page.mdx b/content/docs/operate/identity/user-sources/okta.mdx similarity index 87% rename from app/en/operate/identity/user-sources/okta/page.mdx rename to content/docs/operate/identity/user-sources/okta.mdx index 3e76f281f..1702aac77 100644 --- a/app/en/operate/identity/user-sources/okta/page.mdx +++ b/content/docs/operate/identity/user-sources/okta.mdx @@ -1,18 +1,15 @@ --- -title: "Okta" -description: "Use Okta as a User Source so people who already sign in with their Okta account can sign in to your MCP Gateways" +title: Okta +description: Use Okta as a User Source so people who already sign in with their Okta account can sign in to your MCP Gateways --- import Image from "next/image"; -import { Steps } from "nextra/components"; export const IMAGE_SCALE_FACTOR = 1.5; export const OKTA_SCREENSHOT_WIDTH = 1312 / IMAGE_SCALE_FACTOR; export const OKTA_SCREENSHOT_HEIGHT = 1099 / IMAGE_SCALE_FACTOR; -# Okta - -Use Okta as a User Source so the people who already sign in with their Okta account can sign in to your MCP Gateways. This guide walks you through what to do in the Okta admin console. Once you have the values it tells you to copy, [create the User Source in Arcade](/operate/identity/user-sources#create-a-user-source). +Use Okta as a User Source so the people who already sign in with their Okta account can sign in to your MCP Gateways. This guide walks you through what to do in the Okta admin console. Once you have the values it tells you to copy, [create the User Source in Arcade](/en/operate/identity/user-sources#create-a-user-source). ## Before you start @@ -96,13 +93,13 @@ Copy these values into Arcade's User Source form: ## Create the User Source in Arcade -Open the Arcade Dashboard, select your project, navigate to **User Sources**, and click **Create User Source**. Paste the values from the table above. For a walkthrough of the form, see [Create a User Source](/operate/identity/user-sources#create-a-user-source) in the User Sources overview. +Open the Arcade Dashboard, select your project, navigate to **User Sources**, and click **Create User Source**. Paste the values from the table above. For a walkthrough of the form, see [Create a User Source](/en/operate/identity/user-sources#create-a-user-source) in the User Sources overview. -Once the User Source is active, attach it to an MCP Gateway by following [Create via Dashboard](/operate/governance/mcp-gateways/create-via-dashboard) and picking **User Source** under "Non-Arcade Users" in the gateway form. +Once the User Source is active, attach it to an MCP Gateway by following [Create via Dashboard](/en/operate/governance/mcp-gateways/create-via-dashboard) and picking **User Source** under "Non-Arcade Users" in the gateway form. ## Notes - **Custom Okta domains**: If your org uses an Okta-hosted custom domain (for example, `auth.yourcompany.com`), use that hostname in the **Issuer URL** instead of `<your-okta-org>.okta.com`. Your end users will see the custom domain in the sign-in URL. - **Custom Authorization Servers**: This guide uses Okta's built-in **Org Authorization Server**, whose issuer is your Okta org URL. If your team uses a Custom Authorization Server for this app (for example, the default `oauth2/default` server, or one you've configured under **Security** → **API** → **Authorization Servers**), use that server's **Issuer URI** instead. Custom Authorization Servers are useful when you need to customize scopes, claims, or access policies. -- **Client secret rotation**: Okta lets you generate a new client secret on the same Client Credentials panel and keep the old one active until you're ready to switch. See [Rotate the client secret](/operate/identity/user-sources#rotate-the-client-secret) in the User Sources guide for the Arcade side. -- **Custom subject claims**: Arcade's default Subject Claim, `sub`, is the user's Okta user ID. If you need Arcade to identify users by a different value, configure a custom claim on a [Custom Authorization Server](https://developer.okta.com/docs/guides/customize-tokens-returned-from-okta/main/) and set the User Source's **Subject Claim** to its name. Arcade only reads ID tokens, so the claim must be on the ID token (not the access token). See [Advanced: scopes and subject claim](/operate/identity/user-sources#advanced-scopes-and-subject-claim) in the overview for what makes a good claim. +- **Client secret rotation**: Okta lets you generate a new client secret on the same Client Credentials panel and keep the old one active until you're ready to switch. See [Rotate the client secret](/en/operate/identity/user-sources#rotate-the-client-secret) in the User Sources guide for the Arcade side. +- **Custom subject claims**: Arcade's default Subject Claim, `sub`, is the user's Okta user ID. If you need Arcade to identify users by a different value, configure a custom claim on a [Custom Authorization Server](https://developer.okta.com/docs/guides/customize-tokens-returned-from-okta/main/) and set the User Source's **Subject Claim** to its name. Arcade only reads ID tokens, so the claim must be on the ID token (not the access token). See [Advanced: scopes and subject claim](/en/operate/identity/user-sources#advanced-scopes-and-subject-claim) in the overview for what makes a good claim. diff --git a/app/en/operate/identity/user-sources/stytch/page.mdx b/content/docs/operate/identity/user-sources/stytch.mdx similarity index 88% rename from app/en/operate/identity/user-sources/stytch/page.mdx rename to content/docs/operate/identity/user-sources/stytch.mdx index 281ee9be4..003987d9f 100644 --- a/app/en/operate/identity/user-sources/stytch/page.mdx +++ b/content/docs/operate/identity/user-sources/stytch.mdx @@ -1,18 +1,15 @@ --- -title: "Stytch" -description: "Use Stytch as a User Source so people who already sign in through your Stytch tenant can sign in to your MCP Gateways" +title: Stytch +description: Use Stytch as a User Source so people who already sign in through your Stytch tenant can sign in to your MCP Gateways --- import Image from "next/image"; -import { Callout, Steps } from "nextra/components"; export const IMAGE_SCALE_FACTOR = 1.5; export const STYTCH_SCREENSHOT_WIDTH = 1312 / IMAGE_SCALE_FACTOR; export const STYTCH_SCREENSHOT_HEIGHT = 1099 / IMAGE_SCALE_FACTOR; -# Stytch - -Use Stytch as a User Source so the people who already sign in through your Stytch project can sign in to your MCP Gateways. Stytch's [Connected Apps](https://stytch.com/docs/connected-apps/build-login-flow/login-flow) feature turns your Stytch project into an OIDC identity provider that Arcade can connect to. This guide applies to both Stytch Consumer and B2B projects, where the Connected Apps feature behaves identically. Once you have the values it tells you to copy, [create the User Source in Arcade](/operate/identity/user-sources#create-a-user-source). +Use Stytch as a User Source so the people who already sign in through your Stytch project can sign in to your MCP Gateways. Stytch's [Connected Apps](https://stytch.com/docs/connected-apps/build-login-flow/login-flow) feature turns your Stytch project into an OIDC identity provider that Arcade can connect to. This guide applies to both Stytch Consumer and B2B projects, where the Connected Apps feature behaves identically. Once you have the values it tells you to copy, [create the User Source in Arcade](/en/operate/identity/user-sources#create-a-user-source). ## Before you start @@ -105,13 +102,13 @@ Copy these values into Arcade's User Source form: ## Create the User Source in Arcade -Open the Arcade Dashboard, select your project, navigate to **User Sources**, and click **Create User Source**. Paste the values from the table above. For a walkthrough of the form, see [Create a User Source](/operate/identity/user-sources#create-a-user-source) in the User Sources overview. +Open the Arcade Dashboard, select your project, navigate to **User Sources**, and click **Create User Source**. Paste the values from the table above. For a walkthrough of the form, see [Create a User Source](/en/operate/identity/user-sources#create-a-user-source) in the User Sources overview. -Once the User Source is active, attach it to an MCP Gateway by following [Create via Dashboard](/operate/governance/mcp-gateways/create-via-dashboard) and picking **User Source** under "Non-Arcade Users" in the gateway form. +Once the User Source is active, attach it to an MCP Gateway by following [Create via Dashboard](/en/operate/governance/mcp-gateways/create-via-dashboard) and picking **User Source** under "Non-Arcade Users" in the gateway form. ## Notes - **Test vs production**: New Connected Apps default to test mode and issue test-environment credentials. Enable the **Enable for production** toggle on the app's **Details** page when you're ready to issue live tokens, then update the User Source in Arcade with the production Client ID and Client Secret. - **Custom Stytch domains**: If you've configured a [custom domain](https://stytch.com/docs/connected-apps/resources/custom-domains) on the Stytch project, use that hostname as the **Issuer URL** instead of the `<project-domain>.customers.stytch.dev` value. End users see the custom domain on the authorization page. -- **Client secret rotation**: Stytch supports [rotating the client secret](https://stytch.com/docs/api-reference/consumer/api/connected-apps/rotate-secret/start-secret-rotation) for a Connected App from the Dashboard or via the API. The rotation flow keeps the previous secret active while you swap the new one into Arcade. See [Rotate the client secret](/operate/identity/user-sources#rotate-the-client-secret) in the User Sources guide for the Arcade-side procedure. -- **Subject Claim**: Arcade's default Subject Claim, `sub`, is the Stytch user's stable identifier and is the recommended value. Stytch's Connected Apps issues a [fixed set of standard OIDC claims](https://stytch.com/docs/api-reference/consumer/api/connected-apps/tokens/connected-app-id-token-object) on the ID token. Stytch does not currently support a custom-claim template for the ID token, so the standard claim set is what Arcade has available. See [Advanced: scopes and subject claim](/operate/identity/user-sources#advanced-scopes-and-subject-claim) in the overview for what makes a good claim. +- **Client secret rotation**: Stytch supports [rotating the client secret](https://stytch.com/docs/api-reference/consumer/api/connected-apps/rotate-secret/start-secret-rotation) for a Connected App from the Dashboard or via the API. The rotation flow keeps the previous secret active while you swap the new one into Arcade. See [Rotate the client secret](/en/operate/identity/user-sources#rotate-the-client-secret) in the User Sources guide for the Arcade-side procedure. +- **Subject Claim**: Arcade's default Subject Claim, `sub`, is the Stytch user's stable identifier and is the recommended value. Stytch's Connected Apps issues a [fixed set of standard OIDC claims](https://stytch.com/docs/api-reference/consumer/api/connected-apps/tokens/connected-app-id-token-object) on the ID token. Stytch does not currently support a custom-claim template for the ID token, so the standard claim set is what Arcade has available. See [Advanced: scopes and subject claim](/en/operate/identity/user-sources#advanced-scopes-and-subject-claim) in the overview for what makes a good claim. diff --git a/content/docs/operate/index.mdx b/content/docs/operate/index.mdx new file mode 100644 index 000000000..78850a533 --- /dev/null +++ b/content/docs/operate/index.mdx @@ -0,0 +1,69 @@ +--- +title: Operate Arcade +sidebarTitle: Overview +description: Deploy, secure, and govern Arcade for your organization. Written for platform operators. +--- + +These docs are for **platform operators**: the people who deploy Arcade, connect identity providers, and govern how agents use tools across a company. + +If you build agents or custom MCP servers, start with [Get Started](/en/get-started/about-arcade) and [Build](/en/build). + +## Choose your path + +<Cards num={2}> + <Cards.Card + title="Quickstart: stand up Arcade for your org" + href="/en/operate/quickstart" + /> + <Cards.Card + title="Compare hosting options" + href="/en/operate/deploy" + /> +</Cards> + +## What operators configure + +### Deploy + +Run Arcade Cloud, deploy through a cloud marketplace, or self-host with Helm. + +<Cards num={2}> + <Cards.Card title="Deployment & hosting" href="/en/operate/deploy" /> + <Cards.Card title="Arcade Cloud" href="/en/operate/deploy/arcade-cloud" /> + <Cards.Card title="Self-host with Helm" href="/en/operate/deploy/helm" /> + <Cards.Card title="Hybrid MCP servers" href="/en/operate/deploy/on-prem" /> +</Cards> + +### Identity + +Connect your end-user identity provider and configure how gateways authenticate users. + +<Cards num={2}> + <Cards.Card title="User Sources" href="/en/operate/identity/user-sources" /> + <Cards.Card title="Auth providers" href="/en/references/auth-providers" /> + <Cards.Card + title="Secure auth in production" + href="/en/build/user-facing-agents/secure-auth-production" + /> +</Cards> + +### Governance + +Federate tools, enforce access policies, and review administrative actions and the tool runs they produced. + +<Cards num={2}> + <Cards.Card title="MCP Gateways" href="/en/operate/governance/mcp-gateways" /> + <Cards.Card + title="Remote MCP servers" + href="/en/operate/governance/remote-mcp-servers" + /> + <Cards.Card title="Contextual Access" href="/en/operate/governance/contextual-access" /> + <Cards.Card title="Audit Logs" href="/en/operate/governance/audit-logs" /> + <Cards.Card title="Tool Executions" href="/en/operate/governance/tool-executions" /> +</Cards> + +## Related references + +- [Glossary](/en/resources/glossary): Account, Tenant, Project, User, and User Source +- [API reference](/en/references/api): Administrator and runtime APIs +- [FAQ](/en/resources/faq): common operator and developer questions diff --git a/content/docs/operate/meta.json b/content/docs/operate/meta.json new file mode 100644 index 000000000..812f3954c --- /dev/null +++ b/content/docs/operate/meta.json @@ -0,0 +1,5 @@ +{ + "title": "Operate", + "icon": "ServerCog", + "pages": ["index", "quickstart", "deploy", "identity", "governance"] +} diff --git a/app/en/operate/quickstart/page.mdx b/content/docs/operate/quickstart.mdx similarity index 61% rename from app/en/operate/quickstart/page.mdx rename to content/docs/operate/quickstart.mdx index 043c3d227..1978de449 100644 --- a/app/en/operate/quickstart/page.mdx +++ b/content/docs/operate/quickstart.mdx @@ -1,16 +1,14 @@ --- -title: "Stand up Arcade for your org" +title: Stand up Arcade for your org +sidebarTitle: Quickstart description: "Platform operator quickstart: deploy Arcade, connect identity, create a gateway, and turn on governance" --- -import { Steps, Callout, Cards } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; - -# Stand up Arcade for your org +import { SignupLink } from "@/components/analytics"; This quickstart is for **platform operators** who set up Arcade for a team or company. You will pick a hosting model, connect identity, create an MCP Gateway, and enable governance. -Agent developers who want to call tools should use the [agent quickstart](/get-started/quickstarts/call-tool-agent) instead. +Agent developers who want to call tools should use the [agent quickstart](/en/get-started/quickstarts/call-tool-agent) instead. <GuideOverview> <GuideOverview.Outcomes> @@ -46,25 +44,25 @@ Pick the hosting model that matches your compliance and ops model: | Option | When to use | Guide | | --- | --- | --- | -| **Arcade Cloud** | Quickest path; Arcade manages the platform | [Arcade Cloud](/operate/deploy/arcade-cloud) | -| **Cloud marketplace** | Arcade-operated platform in your cloud account | [Azure](/operate/deploy/azure), [AWS](/operate/deploy/aws), [GCP](/operate/deploy/gcp) | -| **Self-host with Helm** | Full control on your Kubernetes cluster | [Helm](/operate/deploy/helm) | +| **Arcade Cloud** | Quickest path; Arcade manages the platform | [Arcade Cloud](/en/operate/deploy/arcade-cloud) | +| **Cloud marketplace** | Arcade-operated platform in your cloud account | [Azure](/en/operate/deploy/azure), [AWS](/en/operate/deploy/aws), [GCP](/en/operate/deploy/gcp) | +| **Self-host with Helm** | Full control on your Kubernetes cluster | [Helm](/en/operate/deploy/helm) | -See the [hosting options overview](/operate/deploy) for a fuller comparison. +See the [hosting options overview](/en/operate/deploy) for a fuller comparison. <Callout type="info"> -Hosting *your* MCP server with [`arcade deploy`](/build/arcade-deploy) is a developer workflow. It does not deploy the Arcade platform. +Hosting *your* MCP server with [`arcade deploy`](/en/build/arcade-deploy) is a developer workflow. It does not deploy the Arcade platform. </Callout> ### Create administrator access 1. <SignupLink linkLocation="docs:operate-quickstart" utmCampaign="docs-operate-quickstart">Sign up for Arcade</SignupLink> (Cloud) or finish your marketplace/Helm install. -2. Create a [project API key](/get-started/setup/api-keys) for administrative use. +2. Create a [project API key](/en/get-started/setup/api-keys) for administrative use. 3. Confirm you can open the Dashboard for your environment. ### Connect end-user identity -For production gateways, connect your OIDC provider as a [User Source](/operate/identity/user-sources) (Okta, Entra ID, Auth0, Clerk, Stytch, or similar). +For production gateways, connect your OIDC provider as a [User Source](/en/operate/identity/user-sources) (Okta, Entra ID, Auth0, Clerk, Stytch, or similar). Arcade Auth is fine for internal testing. Use a User Source when end users already live in your identity system. @@ -72,27 +70,27 @@ Arcade Auth is fine for internal testing. Use a User Source when end users alrea Create a gateway that federates the tools your agents and MCP clients should see: -- [Create via Dashboard](/operate/governance/mcp-gateways/create-via-dashboard) -- [Create via AI Assistant](/operate/governance/mcp-gateways/create-via-ai) +- [Create via Dashboard](/en/operate/governance/mcp-gateways/create-via-dashboard) +- [Create via AI Assistant](/en/operate/governance/mcp-gateways/create-via-ai) -Tools can come from Arcade's pre-built catalog, an MCP server you deploy, or a [remote MCP server](/operate/governance/remote-mcp-servers) you register — a gateway mixes all three. +Tools can come from Arcade's pre-built catalog, an MCP server you deploy, or a [remote MCP server](/en/operate/governance/remote-mcp-servers) you register — a gateway mixes all three. Attach your User Source when you configure authentication for production. -To add the gateway in Claude for your organization, an Owner adds the gateway URL as a custom connector and keeps the **Detected** defaults. See [Keep the detected defaults](/get-started/mcp-clients/claude-desktop#keep-the-detected-defaults). +To add the gateway in Claude for your organization, an Owner adds the gateway URL as a custom connector and keeps the **Detected** defaults. See [Keep the detected defaults](/en/get-started/mcp-clients/claude-desktop#keep-the-detected-defaults). ### Turn on governance -- **Contextual Access**: control tool visibility and behavior with hooks. Start with [Contextual Access](/operate/governance/contextual-access). -- **Audit Logs**: Arcade captures administrative actions by default. See [Audit Logs](/operate/governance/audit-logs) for the dashboard and SIEM API. +- **Contextual Access**: control tool visibility and behavior with hooks. Start with [Contextual Access](/en/operate/governance/contextual-access). +- **Audit Logs**: Arcade captures administrative actions by default. See [Audit Logs](/en/operate/governance/audit-logs) for the dashboard and SIEM API. </Steps> ## Next steps <Cards num={2}> - <Cards.Card title="Operate overview" href="/operate" /> - <Cards.Card title="MCP Gateways" href="/operate/governance/mcp-gateways" /> - <Cards.Card title="User Sources" href="/operate/identity/user-sources" /> - <Cards.Card title="Deployment & hosting" href="/operate/deploy" /> + <Cards.Card title="Operate overview" href="/en/operate" /> + <Cards.Card title="MCP Gateways" href="/en/operate/governance/mcp-gateways" /> + <Cards.Card title="User Sources" href="/en/operate/identity/user-sources" /> + <Cards.Card title="Deployment & hosting" href="/en/operate/deploy" /> </Cards> diff --git a/app/en/references/api/page.mdx b/content/docs/references/api.mdx similarity index 72% rename from app/en/references/api/page.mdx rename to content/docs/references/api.mdx index f315b79cd..c90562631 100644 --- a/app/en/references/api/page.mdx +++ b/content/docs/references/api.mdx @@ -1,13 +1,12 @@ --- -title: "Arcade API Reference" -description: "Learn about the Arcade API. It's swagger time!" +title: Arcade API Reference +sidebarTitle: API +description: Learn about the Arcade API. It's swagger time! --- import SwaggerUI from "swagger-ui-react"; import "swagger-ui-react/swagger-ui.css"; -import "./swaggerHacks.css"; - -# Arcade API Reference +import "@/components/pages/references/api/swaggerHacks.css"; The base URL for all API requests is `https://api.arcade.dev`. diff --git a/app/en/references/arcade-cli/page.mdx b/content/docs/references/arcade-cli.mdx similarity index 99% rename from app/en/references/arcade-cli/page.mdx rename to content/docs/references/arcade-cli.mdx index 61c26a038..adc84a8b2 100644 --- a/app/en/references/arcade-cli/page.mdx +++ b/content/docs/references/arcade-cli.mdx @@ -1,12 +1,10 @@ --- -title: "Arcade CLI" -description: "Learn how to install and use the Arcade CLI" +title: The Arcade CLI +metaTitle: Arcade CLI +sidebarTitle: Arcade CLI +description: Learn how to install and use the Arcade CLI --- -import { Tabs, Callout } from "nextra/components"; - -# The Arcade CLI - The Arcade CLI is a command-line tool that allows you to manage your Arcade deployments, generate, test, and manage your MCP servers, and more. ## Install the Arcade CLI @@ -36,7 +34,7 @@ In your terminal, run the following command to install the published `arcade-mcp </Tabs> <Callout type="info"> - Using Windows and PowerShell? Follow the [Windows environment setup](/get-started/setup/windows-environment) guide for install options with and without `uv`. + Using Windows and PowerShell? Follow the [Windows environment setup](/en/get-started/setup/windows-environment) guide for install options with and without `uv`. </Callout> ## Upgrade the Arcade CLI @@ -68,7 +66,7 @@ arcade --version ``` <Callout type="info"> -If you previously had the `arcade-ai` package installed, you should uninstall it first. The old `arcade-ai` CLI has been replaced by `arcade-mcp`. See the [migration guide](/build/create-tools/migrate-toolkits) for details on migrating from legacy toolkits. +If you previously had the `arcade-ai` package installed, you should uninstall it first. The old `arcade-ai` CLI has been replaced by `arcade-mcp`. See the [migration guide](/en/build/create-tools/migrate-toolkits) for details on migrating from legacy toolkits. </Callout> ### Re-authenticate after upgrading diff --git a/app/en/references/auth-providers/airtable/page.mdx b/content/docs/references/auth-providers/airtable.mdx similarity index 89% rename from app/en/references/auth-providers/airtable/page.mdx rename to content/docs/references/auth-providers/airtable.mdx index 4b79c27eb..eaa10c412 100644 --- a/app/en/references/auth-providers/airtable/page.mdx +++ b/content/docs/references/auth-providers/airtable.mdx @@ -1,13 +1,13 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Airtable +--- +title: Airtable +--- The Airtable auth provider enables tools and agents to call [Airtable APIs](https://airtable.com/developers/web/api/introduction) on behalf of a user using OAuth 2.0 authentication. <Callout> Want to quickly get started with Airtable in your agent or AI app? The pre-built [Arcade Airtable MCP - Server](/resources/integrations/productivity/airtable) is what you want! + Server](/en/resources/integrations/productivity/airtable) is what you want! </Callout> ### What's documented here @@ -16,7 +16,7 @@ This page describes how to use and configure Airtable auth with Arcade. This auth provider is used by: -- The [Arcade Airtable MCP Server](/resources/integrations/productivity/airtable), which provides pre-built tools for interacting with Airtable +- The [Arcade Airtable MCP Server](/en/resources/integrations/productivity/airtable), which provides pre-built tools for interacting with Airtable - Your [app code](#using-airtable-auth-in-app-code) that needs to call the Airtable API - Or, your [custom tools](#using-airtable-auth-in-custom-tools) that need to call the Airtable API @@ -24,14 +24,14 @@ This auth provider is used by: Arcade offers a default Airtable auth provider that you can use in the Arcade Cloud Platform. In this case, your users will see `Arcade` as the name of the app requesting permission. -If you choose to use Arcade's Airtable auth, you don't need to configure anything. Follow the [Airtable MCP Server examples](/resources/integrations/productivity/airtable) to get started calling Airtable tools. +If you choose to use Arcade's Airtable auth, you don't need to configure anything. Follow the [Airtable MCP Server examples](/en/resources/integrations/productivity/airtable) to get started calling Airtable tools. ## Configuring Airtable auth <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -120,7 +120,7 @@ Hit the **Create** button and the provider will be ready to be used. <Callout type="info"> This method is only available when you are [self-hosting the - engine](/operate/deploy/helm) + engine](/en/operate/deploy/helm) </Callout> <Steps> @@ -164,11 +164,11 @@ Setting `provider_id: airtable` applies the Arcade Engine's built-in Airtable co </Tabs.Tab> </Tabs> -When you use tools that require Airtable auth using your Arcade account credentials, Arcade will automatically use this Airtable OAuth provider. If you have multiple Airtable providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Airtable auth using your Arcade account credentials, Arcade will automatically use this Airtable OAuth provider. If you have multiple Airtable providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. ## Using Airtable auth in app code -Use the Airtable auth provider in your own agents and AI apps to get a user token for the Airtable API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Airtable auth provider in your own agents and AI apps to get a user token for the Airtable API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the Airtable API: @@ -234,9 +234,9 @@ const token = authResponse.context.token; ## Using Airtable auth in custom tools -You can use the pre-built [Arcade Airtable MCP Server](/resources/integrations/productivity/airtable) to quickly build agents and AI apps that interact with Airtable. +You can use the pre-built [Arcade Airtable MCP Server](/en/resources/integrations/productivity/airtable) to quickly build agents and AI apps that interact with Airtable. -If the pre-built tools in the Airtable MCP Server don't meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Airtable API. +If the pre-built tools in the Airtable MCP Server don't meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Airtable API. Use the `Airtable()` auth class to specify that a tool requires authorization with Airtable. The `context.authorization.token` field will be automatically populated with the user's Airtable token: diff --git a/app/en/references/auth-providers/asana/page.mdx b/content/docs/references/auth-providers/asana.mdx similarity index 87% rename from app/en/references/auth-providers/asana/page.mdx rename to content/docs/references/auth-providers/asana.mdx index f72408aba..80e06af6b 100644 --- a/app/en/references/auth-providers/asana/page.mdx +++ b/content/docs/references/auth-providers/asana.mdx @@ -1,12 +1,12 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Asana +--- +title: Asana +--- The Asana auth provider enables tools and agents to call Asana APIs on behalf of a user. <Callout> Want to quickly get started with Asana services in your agent or AI app? The - pre-built [Arcade Asana MCP Server](/resources/integrations/productivity/asana) is what you + pre-built [Arcade Asana MCP Server](/en/resources/integrations/productivity/asana) is what you want! </Callout> @@ -16,7 +16,7 @@ This page describes how to use and configure Asana auth with Arcade. This auth provider is used by: -- The [Arcade Asana MCP Server](/resources/integrations/productivity/asana), which provides pre-built tools for interacting with Asana +- The [Arcade Asana MCP Server](/en/resources/integrations/productivity/asana), which provides pre-built tools for interacting with Asana - Your [app code](#using-asana-auth-in-app-code) that needs to call Asana APIs - Or, your [custom tools](#using-asana-auth-in-custom-tools) that need to call Asana APIs @@ -24,14 +24,14 @@ This auth provider is used by: Arcade offers a default Asana auth provider that you can use in the Arcade Cloud Platform. In this case, your users will see `Arcade` as the name of the application that's requesting permission. -If you choose to use Arcade's Asana auth, you don't need to configure anything. Follow the [Asana MCP Server examples](/resources/integrations/productivity/asana) to get started calling Asana tools. +If you choose to use Arcade's Asana auth, you don't need to configure anything. Follow the [Asana MCP Server examples](/en/resources/integrations/productivity/asana) to get started calling Asana tools. ## Use Your Own Asana App Credentials <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -99,20 +99,20 @@ Hit the **Create** button and the provider will be ready to be used. </Steps> -When you use tools that require Asana auth using your Arcade account credentials, Arcade will automatically use this Asana OAuth provider. If you have multiple Asana providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Asana auth using your Arcade account credentials, Arcade will automatically use this Asana OAuth provider. If you have multiple Asana providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. </Tabs.Tab> </Tabs> ## Using the Arcade Asana MCP Servers -The [Arcade Asana MCP Server](/resources/integrations/productivity/asana) provides tools to interact with various Asana objects, such as tasks, projects, teams, and users. +The [Arcade Asana MCP Server](/en/resources/integrations/productivity/asana) provides tools to interact with various Asana objects, such as tasks, projects, teams, and users. -Refer to the [MCP Server documentation and examples](/resources/integrations/productivity/asana) to learn how to use the MCP Server to build agents and AI apps that interact with Asana services. +Refer to the [MCP Server documentation and examples](/en/resources/integrations/productivity/asana) to learn how to use the MCP Server to build agents and AI apps that interact with Asana services. ## Using Asana auth in app code -Use the Asana auth provider in your own agents and AI apps to get a user-scoped token for the Asana API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Asana auth provider in your own agents and AI apps to get a user-scoped token for the Asana API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the Asana API: @@ -187,9 +187,9 @@ You can use the auth token to call the [Get multiple tasks endpoint](https://dev ## Using Asana auth in custom tools -You can use the pre-built [Arcade Asana MCP Server](/resources/integrations/productivity/asana) to quickly build agents and AI apps that interact with Asana. +You can use the pre-built [Arcade Asana MCP Server](/en/resources/integrations/productivity/asana) to quickly build agents and AI apps that interact with Asana. -If the pre-built tools in the Asana MCP Server don't meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with Asana API. +If the pre-built tools in the Asana MCP Server don't meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with Asana API. Use the `Asana()` auth class to specify that a tool requires authorization with Asana. The authentication token needed to call the Asana API is available in the tool context through the `context.get_auth_token_or_empty()` method. diff --git a/app/en/references/auth-providers/atlassian/page.mdx b/content/docs/references/auth-providers/atlassian.mdx similarity index 86% rename from app/en/references/auth-providers/atlassian/page.mdx rename to content/docs/references/auth-providers/atlassian.mdx index 140800f9b..5188bfbaa 100644 --- a/app/en/references/auth-providers/atlassian/page.mdx +++ b/content/docs/references/auth-providers/atlassian.mdx @@ -1,6 +1,6 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Atlassian +--- +title: Atlassian +--- The Atlassian auth provider enables tools and agents to call the Atlassian API on behalf of a user. @@ -10,7 +10,7 @@ This page describes how to use and configure Atlassian auth with Arcade. This auth provider is used by: -- The [Arcade Jira MCP Server](/resources/integrations/productivity/jira) and [Arcade Confluence MCP Server](/resources/integrations/productivity/confluence), which provide pre-built tools for interacting with Jira and Confluence +- The [Arcade Jira MCP Server](/en/resources/integrations/productivity/jira) and [Arcade Confluence MCP Server](/en/resources/integrations/productivity/confluence), which provide pre-built tools for interacting with Jira and Confluence - Your [app code](#using-atlassian-auth-in-app-code) that needs to call Atlassian APIs - Or, your [custom tools](#using-atlassian-auth-in-custom-tools) that need to call Atlassian APIs @@ -18,14 +18,14 @@ This auth provider is used by: Arcade offers a default Atlassian auth provider that you can use in the Arcade Cloud Platform. In this case, your users will see `Arcade` as the name of the app requesting permission. -If you choose to use Arcade's Atlassian auth, you don't need to configure anything. Follow the [Jira](/resources/integrations/productivity/jira) or [Confluence](/resources/integrations/productivity/confluence) MCP Server examples to get started calling Atlassian tools. +If you choose to use Arcade's Atlassian auth, you don't need to configure anything. Follow the [Jira](/en/resources/integrations/productivity/jira) or [Confluence](/en/resources/integrations/productivity/confluence) MCP Server examples to get started calling Atlassian tools. ## Configuring Atlassian auth <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -76,14 +76,14 @@ Hit the **Create** button and the provider will be ready to be used. </Steps> -When you use tools that require Atlassian auth using your Arcade account credentials, Arcade will automatically use this Atlassian OAuth provider. If you have multiple Atlassian providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Atlassian auth using your Arcade account credentials, Arcade will automatically use this Atlassian OAuth provider. If you have multiple Atlassian providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. </Tabs.Tab> </Tabs> ## Using Atlassian auth in app code -Use the Atlassian auth provider in your own agents and AI apps to get a user token for the Atlassian API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Atlassian auth provider in your own agents and AI apps to get a user token for the Atlassian API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the Atlassian API: @@ -149,7 +149,7 @@ const token = authResponse.context.token; ## Using Atlassian auth in custom tools -You can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Atlassian API. +You can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Atlassian API. Use the `Atlassian()` auth class to specify that a tool requires authorization with Atlassian. The `context.authorization.token` field will be automatically populated with the user's Atlassian token: diff --git a/app/en/references/auth-providers/attio/page.mdx b/content/docs/references/auth-providers/attio.mdx similarity index 84% rename from app/en/references/auth-providers/attio/page.mdx rename to content/docs/references/auth-providers/attio.mdx index 77940c45e..565365b24 100644 --- a/app/en/references/auth-providers/attio/page.mdx +++ b/content/docs/references/auth-providers/attio.mdx @@ -1,12 +1,12 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Attio +--- +title: Attio +--- The Attio auth provider enables tools and agents to call [Attio APIs](https://docs.attio.com/rest-api/overview) on behalf of a user. <Callout> Want to quickly get started with Attio in your agent or AI app? The pre-built - [Arcade Attio MCP server](/resources/integrations/sales/attio) is what you + [Arcade Attio MCP server](/en/resources/integrations/sales/attio) is what you want. </Callout> @@ -16,7 +16,7 @@ This page describes how to use and configure Attio auth with Arcade. Use this auth provider with: -- The [Arcade Attio MCP server](/resources/integrations/sales/attio), which provides pre-built tools for interacting with Attio +- The [Arcade Attio MCP server](/en/resources/integrations/sales/attio), which provides pre-built tools for interacting with Attio - Your [app code](#using-attio-auth-in-app-code) that needs to call Attio APIs - Or, your [custom tools](#using-attio-auth-in-custom-tools) that need to call Attio APIs @@ -24,14 +24,14 @@ Use this auth provider with: Arcade offers a default Attio auth provider that you can use in the Arcade Cloud Platform. In this case, your users will see `Arcade` as the name of the application requesting permission. -If you choose to use Arcade's Attio auth, you don't need to configure anything. Follow the [Attio MCP server examples](/resources/integrations/sales/attio) to get started calling Attio tools. +If you choose to use Arcade's Attio auth, you don't need to configure anything. Follow the [Attio MCP server examples](/en/resources/integrations/sales/attio) to get started calling Attio tools. ## Use your own Attio app credentials <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -52,7 +52,7 @@ Before showing how to configure your Attio app credentials, we'll go through the - In your app's settings, navigate to the **OAuth** tab - Toggle **Enable OAuth 2.0** at the top of the page - Under **Redirect URIs**, add the redirect URL generated by Arcade (see below) -- Under **Scopes**, enable the scopes your app needs. If you plan to use the [Arcade Attio MCP server](/resources/integrations/sales/attio), enable: +- Under **Scopes**, enable the scopes your app needs. If you plan to use the [Arcade Attio MCP server](/en/resources/integrations/sales/attio), enable: - `call_recording:read` - `list_configuration:read-write` - `list_entry:read-write` @@ -103,20 +103,20 @@ Hit the **Create** button and the provider will be ready for use. </Steps> -When you use tools that require Attio auth using your Arcade account credentials, Arcade will automatically use this Attio OAuth provider. If you have multiple Attio providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Attio auth using your Arcade account credentials, Arcade will automatically use this Attio OAuth provider. If you have multiple Attio providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. </Tabs.Tab> </Tabs> ## Using the Arcade Attio MCP server -The [Arcade Attio MCP server](/resources/integrations/sales/attio) provides tools to interact with Attio CRM objects, such as records, notes, tasks, lists, and meetings. +The [Arcade Attio MCP server](/en/resources/integrations/sales/attio) provides tools to interact with Attio CRM objects, such as records, notes, tasks, lists, and meetings. -Refer to the [MCP server documentation and examples](/resources/integrations/sales/attio) to learn how to use the MCP server to build agents and AI apps that interact with Attio. +Refer to the [MCP server documentation and examples](/en/resources/integrations/sales/attio) to learn how to use the MCP server to build agents and AI apps that interact with Attio. ## Using Attio auth in app code -Use the Attio auth provider in your own agents and AI apps to get a user-scoped token for the Attio API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Attio auth provider in your own agents and AI apps to get a user-scoped token for the Attio API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the Attio API: @@ -184,7 +184,7 @@ const token = authResponse.context.token; ## Using Attio auth in custom tools -You can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Attio API. +You can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Attio API. Use the `Attio()` auth class to specify that a tool requires authorization with Attio. The authentication token needed to call the Attio API is available in the tool context through the `context.get_auth_token_or_empty()` method. diff --git a/app/en/references/auth-providers/calendly/page.mdx b/content/docs/references/auth-providers/calendly.mdx similarity index 88% rename from app/en/references/auth-providers/calendly/page.mdx rename to content/docs/references/auth-providers/calendly.mdx index 790440d64..d5d4df406 100644 --- a/app/en/references/auth-providers/calendly/page.mdx +++ b/content/docs/references/auth-providers/calendly.mdx @@ -1,13 +1,13 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Calendly +--- +title: Calendly +--- The Calendly auth provider enables tools and agents to call [Calendly APIs](https://developer.calendly.com/api-docs) on behalf of a user using OAuth 2.0 authentication. <Callout> Want to quickly get started with Calendly in your agent or AI app? The pre-built [Arcade Calendly MCP - Server](/resources/integrations/productivity/calendly) is what you want! + Server](/en/resources/integrations/productivity/calendly) is what you want! </Callout> ### What's documented here @@ -16,7 +16,7 @@ This page describes how to use and configure Calendly auth with Arcade. This auth provider is used by: -- The [Arcade Calendly MCP Server](/resources/integrations/productivity/calendly), which provides pre-built tools for interacting with Calendly +- The [Arcade Calendly MCP Server](/en/resources/integrations/productivity/calendly), which provides pre-built tools for interacting with Calendly - Your [app code](#using-calendly-auth-in-app-code) that needs to call the Calendly API - Or, your [custom tools](#using-calendly-auth-in-custom-tools) that need to call the Calendly API @@ -24,14 +24,14 @@ This auth provider is used by: Arcade offers a default Calendly auth provider that you can use in the Arcade Cloud Platform. In this case, your users will see `Arcade` as the name of the app requesting permission. -If you choose to use Arcade's Calendly auth, you don't need to configure anything. Follow the [Calendly MCP Server examples](/resources/integrations/productivity/calendly) to get started calling Calendly tools. +If you choose to use Arcade's Calendly auth, you don't need to configure anything. Follow the [Calendly MCP Server examples](/en/resources/integrations/productivity/calendly) to get started calling Calendly tools. ## Configuring Calendly auth <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -115,7 +115,7 @@ Hit the **Create** button and the provider will be ready to be used. <Callout type="info"> This method is only available when you are [self-hosting the - engine](/operate/deploy/helm) + engine](/en/operate/deploy/helm) </Callout> <Steps> @@ -159,11 +159,11 @@ Setting `provider_id: calendly` applies the Arcade Engine's built-in Calendly co </Tabs.Tab> </Tabs> -When you use tools that require Calendly auth using your Arcade account credentials, Arcade will automatically use this Calendly OAuth provider. If you have multiple Calendly providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Calendly auth using your Arcade account credentials, Arcade will automatically use this Calendly OAuth provider. If you have multiple Calendly providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. ## Using Calendly auth in app code -Use the Calendly auth provider in your own agents and AI apps to get a user token for the Calendly API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Calendly auth provider in your own agents and AI apps to get a user token for the Calendly API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the Calendly API: @@ -226,9 +226,9 @@ const token = authResponse.context.token; ## Using Calendly auth in custom tools -You can use the pre-built [Arcade Calendly MCP Server](/resources/integrations/productivity/calendly) to quickly build agents and AI apps that interact with Calendly. +You can use the pre-built [Arcade Calendly MCP Server](/en/resources/integrations/productivity/calendly) to quickly build agents and AI apps that interact with Calendly. -If the pre-built tools in the Calendly MCP Server don't meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Calendly API. +If the pre-built tools in the Calendly MCP Server don't meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Calendly API. Use the `Calendly()` auth class to specify that a tool requires authorization with Calendly. The `context.authorization.token` field will be automatically populated with the user's Calendly token: diff --git a/app/en/references/auth-providers/cisco-duo/page.mdx b/content/docs/references/auth-providers/cisco-duo.mdx similarity index 91% rename from app/en/references/auth-providers/cisco-duo/page.mdx rename to content/docs/references/auth-providers/cisco-duo.mdx index 6b58e5e10..ff441d8bb 100644 --- a/app/en/references/auth-providers/cisco-duo/page.mdx +++ b/content/docs/references/auth-providers/cisco-duo.mdx @@ -3,10 +3,6 @@ title: Cisco Duo description: Authorize tools and agents with Cisco Duo (OIDC) on behalf of a user --- -import { Tabs, Callout, Steps } from "nextra/components"; - -# Cisco Duo - The Cisco Duo auth provider lets tools and agents authorize with Cisco Duo and call your Duo-protected APIs, apps, and systems on behalf of a user. Duo Single Sign-On acts as an OpenID Connect (OIDC) provider, so Arcade runs the standard authorization code flow, including Duo's multi-factor (Duo Push) prompt, and hands your tool a token. <Callout> @@ -23,7 +19,7 @@ The Cisco Duo auth provider lets tools and agents authorize with Cisco Duo and c Cisco Duo is configured as a custom OAuth 2.0 provider. Duo's OAuth/OIDC endpoints are unique to your Duo account, so you bring your own Duo application credentials and endpoints. The generic [OAuth 2.0 - provider](/references/auth-providers/oauth2) page is the underlying + provider](/en/references/auth-providers/oauth2) page is the underlying configuration reference. </Callout> @@ -39,7 +35,7 @@ This page describes how to configure Cisco Duo auth with Arcade, and use it from <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -144,8 +140,8 @@ The most common way to use Cisco Duo with Arcade is to put a Duo-protected syste Build an MCP server whose tools require Cisco Duo auth (see [Create your own Duo-authorized tools](#create-your-own-duo-authorized-tools)), then make it available in Arcade by either: -- Deploying a [custom MCP server](/build/create-tools/tool-basics/build-mcp-server) to Arcade with [`arcade deploy`](/build/arcade-deploy), or -- [Adding an existing remote MCP server](/operate/governance/remote-mcp-servers) to your Arcade project. +- Deploying a [custom MCP server](/en/build/create-tools/tool-basics/build-mcp-server) to Arcade with [`arcade deploy`](/en/build/arcade-deploy), or +- [Adding an existing remote MCP server](/en/operate/governance/remote-mcp-servers) to your Arcade project. Once deployed, your server and its tools appear in the Arcade Dashboard under **Servers**. Each tool shows the Cisco Duo provider it requires: @@ -153,7 +149,7 @@ Once deployed, your server and its tools appear in the Arcade Dashboard under ** ### Connect your agentic client to an MCP gateway -Create an [MCP gateway](/operate/governance/mcp-gateways) that exposes your server's tools, then connect your client to the gateway URL (`https://api.arcade.dev/mcp/<your-gateway>`). +Create an [MCP gateway](/en/operate/governance/mcp-gateways) that exposes your server's tools, then connect your client to the gateway URL (`https://api.arcade.dev/mcp/<your-gateway>`). ### Call a tool @@ -165,7 +161,7 @@ Ask the agent to do something that uses one of your tools. The first time, Arcad ## Create your own Duo-authorized tools -Tools that call the APIs, databases, and enterprise systems protected by Cisco Duo are built like any other Arcade tool, so follow the [Add user authorization to your tools](/build/create-tools/tool-basics/create-tool-auth) and [Build an MCP server](/build/create-tools/tool-basics/build-mcp-server) guides. +Tools that call the APIs, databases, and enterprise systems protected by Cisco Duo are built like any other Arcade tool, so follow the [Add user authorization to your tools](/en/build/create-tools/tool-basics/create-tool-auth) and [Build an MCP server](/en/build/create-tools/tool-basics/build-mcp-server) guides. The only Duo-specific part is the auth requirement on each tool: use the `OAuth2` class with the provider ID you configured and the scopes the tool needs. diff --git a/app/en/references/auth-providers/clickup/page.mdx b/content/docs/references/auth-providers/clickup.mdx similarity index 88% rename from app/en/references/auth-providers/clickup/page.mdx rename to content/docs/references/auth-providers/clickup.mdx index f01929e49..a379036e5 100644 --- a/app/en/references/auth-providers/clickup/page.mdx +++ b/content/docs/references/auth-providers/clickup.mdx @@ -1,6 +1,6 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# ClickUp +--- +title: ClickUp +--- The ClickUp auth provider enables tools and agents to call the ClickUp API on behalf of a user. @@ -10,7 +10,7 @@ This page describes how to use and configure ClickUp auth with Arcade. This auth provider is used by: -- The [Arcade ClickUp MCP Server](/resources/integrations/productivity/clickup), which provides pre-built tools for interacting with ClickUp +- The [Arcade ClickUp MCP Server](/en/resources/integrations/productivity/clickup), which provides pre-built tools for interacting with ClickUp - Your [app code](#using-clickup-auth-in-app-code) that needs to call ClickUp APIs - Or, your [custom tools](#using-clickup-auth-in-custom-tools) that need to call ClickUp APIs @@ -18,14 +18,14 @@ This auth provider is used by: Arcade offers a default ClickUp auth provider that you can use in the Arcade Cloud Platform. In this case, your users will see `Arcade` as the name of the app requesting permission. -If you choose to use Arcade's ClickUp auth, you don't need to configure anything. Follow the [ClickUp MCP Server examples](/resources/integrations/productivity/clickup) to get started calling ClickUp tools. +If you choose to use Arcade's ClickUp auth, you don't need to configure anything. Follow the [ClickUp MCP Server examples](/en/resources/integrations/productivity/clickup) to get started calling ClickUp tools. ## Configuring ClickUp auth <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -76,14 +76,14 @@ Hit the **Create** button and the provider will be ready to be used. </Steps> -When you use tools that require ClickUp auth using your Arcade account credentials, Arcade will automatically use this ClickUp OAuth provider. If you have multiple ClickUp providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require ClickUp auth using your Arcade account credentials, Arcade will automatically use this ClickUp OAuth provider. If you have multiple ClickUp providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. </Tabs.Tab> </Tabs> ## Using ClickUp auth in app code -Use the ClickUp auth provider in your own agents and AI apps to get a user-scoped token for the ClickUp API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the ClickUp auth provider in your own agents and AI apps to get a user-scoped token for the ClickUp API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the ClickUp API: @@ -144,7 +144,7 @@ const { token } = authResponse.context; ## Using ClickUp auth in custom tools -You can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the ClickUp API. +You can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the ClickUp API. Use the `ClickUp()` auth class to specify that a tool requires authorization with ClickUp. The `context.authorization.token` field will be automatically populated with the user's ClickUp token: diff --git a/app/en/references/auth-providers/discord/page.mdx b/content/docs/references/auth-providers/discord.mdx similarity index 92% rename from app/en/references/auth-providers/discord/page.mdx rename to content/docs/references/auth-providers/discord.mdx index 4205294ad..937ab48fe 100644 --- a/app/en/references/auth-providers/discord/page.mdx +++ b/content/docs/references/auth-providers/discord.mdx @@ -1,6 +1,6 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Discord +--- +title: Discord +--- <Callout type="info"> Arcade Cloud includes a default Discord auth provider, so you can start @@ -26,7 +26,7 @@ This auth provider is used by: <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -76,14 +76,14 @@ Hit the **Create** button and the provider will be ready to be used. </Steps> -When you use tools that require Discord auth using your Arcade account credentials, Arcade will automatically use this Discord OAuth provider. If you have multiple Discord providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Discord auth using your Arcade account credentials, Arcade will automatically use this Discord OAuth provider. If you have multiple Discord providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. </Tabs.Tab> </Tabs> ## Using Discord auth in app code -Use the Discord auth provider in your own agents and AI apps to get a user token for the Discord API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Discord auth provider in your own agents and AI apps to get a user token for the Discord API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the Discord API: @@ -149,7 +149,7 @@ const token = authResponse.context.token; ## Using Discord auth in custom tools -You can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Discord API. +You can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Discord API. Use the `Discord()` auth class to specify that a tool requires authorization with Discord. The `context.authorization.token` field will be automatically populated with the user's Discord token: diff --git a/app/en/references/auth-providers/dropbox/page.mdx b/content/docs/references/auth-providers/dropbox.mdx similarity index 90% rename from app/en/references/auth-providers/dropbox/page.mdx rename to content/docs/references/auth-providers/dropbox.mdx index ec736e1fb..ed8e523da 100644 --- a/app/en/references/auth-providers/dropbox/page.mdx +++ b/content/docs/references/auth-providers/dropbox.mdx @@ -1,6 +1,6 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Dropbox +--- +title: Dropbox +--- <Callout type="info"> Arcade Cloud includes a default Dropbox auth provider, so you can start @@ -18,7 +18,7 @@ This page describes how to use and configure Dropbox auth with Arcade. This auth provider is used by: -- The [Arcade Dropbox MCP Server](/resources/integrations/productivity/dropbox), which provides pre-built tools for interacting with Dropbox +- The [Arcade Dropbox MCP Server](/en/resources/integrations/productivity/dropbox), which provides pre-built tools for interacting with Dropbox - Your [app code](#using-dropbox-auth-in-app-code) that needs to call Dropbox APIs - Or, your [custom tools](#using-dropbox-auth-in-custom-tools) that need to call Dropbox APIs @@ -27,7 +27,7 @@ This auth provider is used by: <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -79,14 +79,14 @@ Hit the **Create** button and the provider will be ready to be used. </Steps> -When you use tools that require Dropbox auth using your Arcade account credentials, Arcade will automatically use this Dropbox OAuth provider. If you have multiple Dropbox providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Dropbox auth using your Arcade account credentials, Arcade will automatically use this Dropbox OAuth provider. If you have multiple Dropbox providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. </Tabs.Tab> </Tabs> ## Using Dropbox auth in app code -Use the Dropbox auth provider in your own agents and AI apps to get a user-scoped token for the Dropbox API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Dropbox auth provider in your own agents and AI apps to get a user-scoped token for the Dropbox API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the Dropbox API: @@ -152,7 +152,7 @@ const token = authResponse.context.token; ## Using Dropbox auth in custom tools -You can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Dropbox API. +You can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Dropbox API. Use the `Dropbox()` auth class to specify that a tool requires authorization with Dropbox. The `context.authorization.token` field will be automatically populated with the user's Dropbox token: diff --git a/app/en/references/auth-providers/figma/page.mdx b/content/docs/references/auth-providers/figma.mdx similarity index 90% rename from app/en/references/auth-providers/figma/page.mdx rename to content/docs/references/auth-providers/figma.mdx index 2249fc6fb..4834083ed 100644 --- a/app/en/references/auth-providers/figma/page.mdx +++ b/content/docs/references/auth-providers/figma.mdx @@ -1,12 +1,12 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Figma +--- +title: Figma +--- The Figma auth provider enables tools and agents to call [Figma APIs](https://developers.figma.com/docs/rest-api/) on behalf of a user using OAuth 2.0 authentication. <Callout> Want to quickly get started with Figma in your agent or AI app? The pre-built - [Arcade Figma MCP Server](/resources/integrations/productivity/figma) is what you + [Arcade Figma MCP Server](/en/resources/integrations/productivity/figma) is what you want! </Callout> @@ -16,13 +16,13 @@ This page describes how to use and configure Figma auth with Arcade. This auth provider is used by: -- The [Arcade Figma MCP Server](/resources/integrations/productivity/figma), which provides pre-built tools for interacting with Figma +- The [Arcade Figma MCP Server](/en/resources/integrations/productivity/figma), which provides pre-built tools for interacting with Figma - Your [app code](#using-figma-auth-in-app-code) that needs to call the Figma API - Or, your [custom tools](#using-figma-auth-in-custom-tools) that need to call the Figma API ### Required scopes for the Figma MCP Server -If you're using the [Arcade Figma MCP Server](/resources/integrations/productivity/figma), you'll need to configure these scopes based on which tools you plan to use: +If you're using the [Arcade Figma MCP Server](/en/resources/integrations/productivity/figma), you'll need to configure these scopes based on which tools you plan to use: - `file_content:read` - File structure, pages, nodes, and image exports - `library_content:read` - Published components, styles, and component sets from files @@ -50,7 +50,7 @@ For detailed descriptions of all available Figma OAuth scopes, refer to the [Fig <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -148,7 +148,7 @@ Hit the **Create** button and the provider will be ready to be used. <Callout type="info"> This method is only available when you are [self-hosting the - engine](/operate/deploy/helm). + engine](/en/operate/deploy/helm). </Callout> <Steps> @@ -198,11 +198,11 @@ auth: </Tabs.Tab> </Tabs> -When you use tools that require Figma auth using your Arcade account credentials, Arcade will automatically use this Figma OAuth provider. If you have multiple Figma providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Figma auth using your Arcade account credentials, Arcade will automatically use this Figma OAuth provider. If you have multiple Figma providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. ## Using Figma auth in app code -Use the Figma auth provider in your own agents and AI apps to get a user token for the Figma API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Figma auth provider in your own agents and AI apps to get a user token for the Figma API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the Figma API: @@ -268,9 +268,9 @@ const token = authResponse.context.token; ## Using Figma auth in custom tools -You can use the pre-built [Arcade Figma MCP Server](/resources/integrations/productivity/figma) to quickly build agents and AI apps that interact with Figma. +You can use the pre-built [Arcade Figma MCP Server](/en/resources/integrations/productivity/figma) to quickly build agents and AI apps that interact with Figma. -If the pre-built tools in the Figma MCP Server don't meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Figma API. +If the pre-built tools in the Figma MCP Server don't meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Figma API. Use the `Figma()` auth class to specify that a tool requires authorization with Figma. The `context.authorization.token` field will be automatically populated with the user's Figma token: diff --git a/app/en/references/auth-providers/github/page.mdx b/content/docs/references/auth-providers/github.mdx similarity index 98% rename from app/en/references/auth-providers/github/page.mdx rename to content/docs/references/auth-providers/github.mdx index 570ef58c7..a252fefed 100644 --- a/app/en/references/auth-providers/github/page.mdx +++ b/content/docs/references/auth-providers/github.mdx @@ -1,12 +1,12 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# GitHub +--- +title: GitHub +--- The GitHub auth provider enables tools and agents to call [GitHub APIs](https://docs.github.com/en/rest/overview/resources-in-the-rest-api) on behalf of a user. <Callout> Want to quickly get started with GitHub in your agent or AI app? The pre-built - [Arcade GitHub MCP Server](/resources/integrations/development/github) is what you want! + [Arcade GitHub MCP Server](/en/resources/integrations/development/github) is what you want! </Callout> ## What's documented here @@ -15,7 +15,7 @@ This page describes how to use and configure GitHub auth with Arcade. This auth provider is used by: -- The [Arcade GitHub MCP Server](/resources/integrations/development/github), which provides pre-built tools for interacting with GitHub +- The [Arcade GitHub MCP Server](/en/resources/integrations/development/github), which provides pre-built tools for interacting with GitHub - Your [app code](#using-github-auth-in-app-code) that needs to call the GitHub API - Or, your [custom tools](#using-github-auth-in-custom-tools) that need to call the GitHub API @@ -436,7 +436,7 @@ When an admin uninstalls an app: <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -474,7 +474,7 @@ Click **Create** button. The provider is now ready to use. </Steps> When you use tools requiring GitHub auth with your Arcade account, Arcade automatically uses this provider. -For multiple providers, see [using multiple auth providers](/references/auth-providers#using-multiple-providers-of-the-same-type). +For multiple providers, see [using multiple auth providers](/en/references/auth-providers#using-multiple-providers-of-the-same-type). </Tabs.Tab> </Tabs> @@ -658,7 +658,7 @@ Unlike github.com's fixed rate limits, GHES administrators can configure custom ## Using GitHub Auth in App Code -Use the GitHub auth provider to get a user token for the GitHub API. See [authorizing agents with Arcade](/get-started/about-arcade) for details. +Use the GitHub auth provider to get a user token for the GitHub API. See [authorizing agents with Arcade](/en/get-started/about-arcade) for details. Use `client.auth.start()` to get a user token: @@ -773,7 +773,7 @@ console.log(data.stargazers_count); ## Using GitHub Auth in Custom Tools -Use the pre-built [Arcade GitHub MCP Server](/resources/integrations/development/github) for quick GitHub integration. +Use the pre-built [Arcade GitHub MCP Server](/en/resources/integrations/development/github) for quick GitHub integration. For custom tools, use the `GitHub()` auth class. The `context.authorization.token` field will be automatically populated: @@ -1187,8 +1187,8 @@ Use this checklist to ensure proper setup: ### Arcade Resources -- [Arcade GitHub MCP Server](/resources/integrations/development/github) -- [Arcade Authorization Guide](/get-started/about-arcade) +- [Arcade GitHub MCP Server](/en/resources/integrations/development/github) +- [Arcade Authorization Guide](/en/get-started/about-arcade) - [Video Tutorial: GitHub App Setup](https://www.youtube.com/watch?v=KcO2SruCdt0) --- diff --git a/app/en/references/auth-providers/google/page.mdx b/content/docs/references/auth-providers/google.mdx similarity index 89% rename from app/en/references/auth-providers/google/page.mdx rename to content/docs/references/auth-providers/google.mdx index d3033f106..349061db6 100644 --- a/app/en/references/auth-providers/google/page.mdx +++ b/content/docs/references/auth-providers/google.mdx @@ -1,12 +1,12 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Google +--- +title: Google +--- The Google auth provider enables tools and agents to call Google/Google Workspace APIs on behalf of a user. <Callout> Want to quickly get started with Google services in your agent or AI app? The - pre-built [Arcade Gmail MCP Server](/resources/integrations/productivity/gmail) is what you + pre-built [Arcade Gmail MCP Server](/en/resources/integrations/productivity/gmail) is what you want! </Callout> @@ -16,7 +16,7 @@ This page describes how to use and configure Google auth with Arcade. This auth provider is used by: -- The Arcade Google MCP Servers, which provide pre-built tools for interacting with Google services, including [Gmail](/resources/integrations/productivity/gmail), [Google Calendar](/resources/integrations/productivity/google-calendar), [Google Contacts](/resources/integrations/productivity/google-contacts), [Google Docs](/resources/integrations/productivity/google-docs), [Google Drive](/resources/integrations/productivity/google-drive), [Google Sheets](/resources/integrations/productivity/google-sheets), [Google Slides](/resources/integrations/productivity/google-slides), and Google Forms +- The Arcade Google MCP Servers, which provide pre-built tools for interacting with Google services, including [Gmail](/en/resources/integrations/productivity/gmail), [Google Calendar](/en/resources/integrations/productivity/google-calendar), [Google Contacts](/en/resources/integrations/productivity/google-contacts), [Google Docs](/en/resources/integrations/productivity/google-docs), [Google Drive](/en/resources/integrations/productivity/google-drive), [Google Sheets](/en/resources/integrations/productivity/google-sheets), [Google Slides](/en/resources/integrations/productivity/google-slides), and Google Forms - Your [app code](#using-google-auth-in-app-code) that needs to call Google APIs - Or, your [custom tools](#using-google-auth-in-custom-tools) that need to call Google APIs @@ -80,7 +80,7 @@ The default provider has some limitations: <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -138,14 +138,14 @@ Hit the **Create** button and the provider will be ready to be used. </Steps> -When you use tools that require Google auth using your Arcade account credentials, Arcade will automatically use this Google OAuth provider. If you have multiple Google providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Google auth using your Arcade account credentials, Arcade will automatically use this Google OAuth provider. If you have multiple Google providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. </Tabs.Tab> </Tabs> ## Using Google auth in app code -Use the Google auth provider in your own agents and AI apps to get a user token for Google APIs. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Google auth provider in your own agents and AI apps to get a user token for Google APIs. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for Google APIs: @@ -271,9 +271,9 @@ console.log(emailMessages); ## Using Google auth in custom tools -You can use the pre-built Arcade Google MCP Servers, like [Arcade Gmail MCP Server](/resources/integrations/productivity/gmail), to quickly build agents and AI apps that interact with Google services like Gmail, Calendar, Drive, and more. +You can use the pre-built Arcade Google MCP Servers, like [Arcade Gmail MCP Server](/en/resources/integrations/productivity/gmail), to quickly build agents and AI apps that interact with Google services like Gmail, Calendar, Drive, and more. -If the pre-built tools in the Google MCP Servers don't meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with Google APIs. +If the pre-built tools in the Google MCP Servers don't meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with Google APIs. Use the `Google()` auth class to specify that a tool requires authorization with Google. The `context.authorization.token` field will be automatically populated with the user's Google token: diff --git a/app/en/references/auth-providers/hubspot/page.mdx b/content/docs/references/auth-providers/hubspot.mdx similarity index 89% rename from app/en/references/auth-providers/hubspot/page.mdx rename to content/docs/references/auth-providers/hubspot.mdx index 36808fd1f..22dcf16f5 100644 --- a/app/en/references/auth-providers/hubspot/page.mdx +++ b/content/docs/references/auth-providers/hubspot.mdx @@ -1,12 +1,12 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Hubspot +--- +title: Hubspot +--- The Hubspot auth provider enables tools and agents to call Hubspot APIs on behalf of a user. <Callout> Want to quickly get started with Hubspot services in your agent or AI app? The - pre-built [Arcade Hubspot MCP Server](/resources/integrations/sales/hubspot) is what you + pre-built [Arcade Hubspot MCP Server](/en/resources/integrations/sales/hubspot) is what you want! </Callout> @@ -16,7 +16,7 @@ This page describes how to use and configure Hubspot auth with Arcade. This auth provider is used by: -- The [Arcade Hubspot MCP Server](/resources/integrations/sales/hubspot), which provides pre-built tools for interacting with Hubspot +- The [Arcade Hubspot MCP Server](/en/resources/integrations/sales/hubspot), which provides pre-built tools for interacting with Hubspot - Your [app code](#using-hubspot-auth-in-app-code) that needs to call Hubspot APIs - Or, your [custom tools](#using-hubspot-auth-in-custom-tools) that need to call Hubspot APIs @@ -24,14 +24,14 @@ This auth provider is used by: Arcade offers a default Hubspot auth provider that you can use in the Arcade Cloud Platform. In this case, your users will see `Arcade` as the name of the application that's requesting permission. -If you choose to use Arcade's Hubspot auth, you don't need to configure anything. Follow the [Hubspot MCP Server examples](/resources/integrations/sales/hubspot) to get started calling Hubspot tools. +If you choose to use Arcade's Hubspot auth, you don't need to configure anything. Follow the [Hubspot MCP Server examples](/en/resources/integrations/sales/hubspot) to get started calling Hubspot tools. ## Use Your Own Hubspot App Credentials <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -114,20 +114,20 @@ Hit the **Create** button and the provider will be ready to be used. </Steps> -When you use tools that require Hubspot auth using your Arcade account credentials, Arcade will automatically use this Hubspot OAuth provider. If you have multiple Hubspot providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Hubspot auth using your Arcade account credentials, Arcade will automatically use this Hubspot OAuth provider. If you have multiple Hubspot providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. </Tabs.Tab> </Tabs> ## Using the Arcade Hubspot MCP Servers -The [Arcade Hubspot MCP Server](/resources/integrations/sales/hubspot) provides tools to interact with various Hubspot objects, such as companies, contacts, deals, and email messages. +The [Arcade Hubspot MCP Server](/en/resources/integrations/sales/hubspot) provides tools to interact with various Hubspot objects, such as companies, contacts, deals, and email messages. -Refer to the [MCP Server documentation and examples](/resources/integrations/sales/hubspot) to learn how to use the MCP Server to build agents and AI apps that interact with Hubspot services. +Refer to the [MCP Server documentation and examples](/en/resources/integrations/sales/hubspot) to learn how to use the MCP Server to build agents and AI apps that interact with Hubspot services. ## Using Hubspot auth in app code -Use the Hubspot auth provider in your own agents and AI apps to get a user-scoped token for the Hubspot API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Hubspot auth provider in your own agents and AI apps to get a user-scoped token for the Hubspot API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the Hubspot API: @@ -197,7 +197,7 @@ The scopes supported by the Arcade Hubspot auth provider are the ones [listed ab ## Using Hubspot auth in custom tools -You can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Hubspot API. +You can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Hubspot API. Use the `Hubspot()` auth class to specify that a tool requires authorization with Hubspot. The authentication token needed to call the Hubspot API is available in the tool context through the `context.get_auth_token_or_empty()` method. diff --git a/app/en/references/auth-providers/page.mdx b/content/docs/references/auth-providers/index.mdx similarity index 78% rename from app/en/references/auth-providers/page.mdx rename to content/docs/references/auth-providers/index.mdx index ffb5cf03e..17736180d 100644 --- a/app/en/references/auth-providers/page.mdx +++ b/content/docs/references/auth-providers/index.mdx @@ -1,12 +1,11 @@ --- title: Auth Providers +sidebarTitle: Overview description: Registry of all auth providers available in the Arcade ecosystem --- -# Auth Providers - -import { ToolCard } from "@/app/_components/tool-card"; -import { ContactEmail } from "@/app/_components/contact-email"; +import { ToolCard } from "@/components/tool-card"; +import { ContactEmail } from "@/components/contact-email"; Auth providers enable users to seamlessly and securely allow Arcade tools to access their data. @@ -16,7 +15,7 @@ It can be useful to configure your own auth provider for the following reasons: - You want to use your own brand on the auth screen - You want to isolate your rate limits from other Arcade customers -- You want to use a service that Arcade [does not have a built-in auth provider for](/references/auth-providers/oauth2) +- You want to use a service that Arcade [does not have a built-in auth provider for](/en/references/auth-providers/oauth2) After adding an auth provider used by an Arcade tool, executing the tool will automatically use your auth provider. Even in the Arcade Cloud Platform, your auth provider will take precedence over the arcade-provided auth provider. @@ -33,243 +32,243 @@ For more information on how to customize your auth provider, select an auth prov name="Airtable" image="airtable.svg" summary="Authorize tools and agents with Airtable" - link="/references/auth-providers/airtable" + link="/en/references/auth-providers/airtable" category="Auth" /> <ToolCard name="Asana" image="asana.svg" summary="Authorize tools and agents with Asana" - link="/references/auth-providers/asana" + link="/en/references/auth-providers/asana" category="Auth" /> <ToolCard name="Atlassian" image="atlassian" summary="Authorize tools and agents with Atlassian" - link="/references/auth-providers/atlassian" + link="/en/references/auth-providers/atlassian" category="Auth" /> <ToolCard name="Attio" image="attio.svg" summary="Authorize tools and agents with Attio CRM" - link="/references/auth-providers/attio" + link="/en/references/auth-providers/attio" category="Auth" /> <ToolCard name="Calendly" image="calendly.svg" summary="Authorize tools and agents with Calendly" - link="/references/auth-providers/calendly" + link="/en/references/auth-providers/calendly" category="Auth" /> <ToolCard name="Cisco Duo" image="oauth2" summary="Authorize tools and agents with Cisco Duo (OIDC)" - link="/references/auth-providers/cisco-duo" + link="/en/references/auth-providers/cisco-duo" category="Auth" /> <ToolCard name="ClickUp" image="clickup.svg" summary="Authorize tools and agents with ClickUp" - link="/references/auth-providers/clickup" + link="/en/references/auth-providers/clickup" category="Auth" /> <ToolCard name="Discord" image="discord" summary="Authorize tools and agents with Discord in a user's context" - link="/references/auth-providers/discord" + link="/en/references/auth-providers/discord" category="Auth" /> <ToolCard name="Dropbox" image="dropbox" summary="Authorize tools and agents with Dropbox in a user's context" - link="/references/auth-providers/dropbox" + link="/en/references/auth-providers/dropbox" category="Auth" /> <ToolCard name="Figma" image="figma.svg" summary="Authorize tools and agents with Figma" - link="/references/auth-providers/figma" + link="/en/references/auth-providers/figma" category="Auth" /> <ToolCard name="GitHub" image="github" summary="Authorize tools and agents with GitHub, including private repositories" - link="/references/auth-providers/github" + link="/en/references/auth-providers/github" category="Auth" /> <ToolCard name="Google" image="google" summary="Authorize tools and agents with Google: Gmail, Calendar, YouTube, Drive, and more" - link="/references/auth-providers/google" + link="/en/references/auth-providers/google" category="Auth" /> <ToolCard name="HubSpot" image="hubspot" summary="Authorize tools and agents with HubSpot" - link="/references/auth-providers/hubspot" + link="/en/references/auth-providers/hubspot" category="Auth" /> <ToolCard name="Linear" image="linear.svg" summary="Authorize tools and agents with Linear" - link="/references/auth-providers/linear" + link="/en/references/auth-providers/linear" category="Auth" /> <ToolCard name="LinkedIn" image="linkedin" summary="Authorize tools and agents with LinkedIn" - link="/references/auth-providers/linkedin" + link="/en/references/auth-providers/linkedin" category="Auth" /> <ToolCard name="Mailchimp" image="mailchimp" summary="Authorize tools and agents with Mailchimp" - link="/references/auth-providers/mailchimp" + link="/en/references/auth-providers/mailchimp" category="Auth" /> <ToolCard name="Microsoft" image="msft" summary="Authorize tools and agents with Microsoft Graph" - link="/references/auth-providers/microsoft" + link="/en/references/auth-providers/microsoft" category="Auth" /> <ToolCard name="Microsoft Power BI" image="msft" summary="Authorize tools and agents with the Power BI Service" - link="/references/auth-providers/microsoft-powerbi" + link="/en/references/auth-providers/microsoft-powerbi" category="Auth" /> <ToolCard name="Miro" image="miro.svg" summary="Authorize tools and agents with Miro" - link="/references/auth-providers/miro" + link="/en/references/auth-providers/miro" category="Auth" /> <ToolCard name="Notion" image="notion" summary="Authorize tools and agents with Notion" - link="/references/auth-providers/notion" + link="/en/references/auth-providers/notion" category="Auth" /> <ToolCard name="PagerDuty" image="pagerduty.svg" summary="Authorize tools and agents with PagerDuty" - link="/references/auth-providers/pagerduty" + link="/en/references/auth-providers/pagerduty" category="Auth" /> <ToolCard name="Reddit" image="reddit" summary="Authorize tools and agents with Reddit" - link="/references/auth-providers/reddit" + link="/en/references/auth-providers/reddit" category="Auth" /> <ToolCard name="Salesforce" image="salesforce" summary="Authorize tools and agents with Salesforce" - link="/references/auth-providers/salesforce" + link="/en/references/auth-providers/salesforce" category="Auth" /> <ToolCard name="ServiceNow" image="servicenow.svg" summary="Authorize tools and agents with ServiceNow" - link="/references/auth-providers/servicenow" + link="/en/references/auth-providers/servicenow" category="Auth" /> <ToolCard name="Slack" image="slack" summary="Authorize tools and agents with Slack" - link="/references/auth-providers/slack" + link="/en/references/auth-providers/slack" category="Auth" /> <ToolCard name="Spotify" image="spotify" summary="Authorize tools and agents with Spotify" - link="/references/auth-providers/spotify" + link="/en/references/auth-providers/spotify" category="Auth" /> <ToolCard name="Square" image="square" summary="Authorize tools and agents with Square" - link="/references/auth-providers/square" + link="/en/references/auth-providers/square" category="Auth" /> <ToolCard name="TickTick" image="ticktick.svg" summary="Authorize tools and agents with TickTick" - link="/references/auth-providers/ticktick" + link="/en/references/auth-providers/ticktick" category="Auth" /> <ToolCard name="Twitch" image="twitch" summary="Authorize tools and agents with Twitch" - link="/references/auth-providers/twitch" + link="/en/references/auth-providers/twitch" category="Auth" /> <ToolCard name="X" image="twitter" summary="Authorize tools and agents with X (Twitter)" - link="/references/auth-providers/x" + link="/en/references/auth-providers/x" category="Auth" /> <ToolCard name="Zendesk" image="zendesk" summary="Authorize tools and agents with Zendesk" - link="/references/auth-providers/zendesk" + link="/en/references/auth-providers/zendesk" category="Auth" /> <ToolCard name="Zoho" image="zoho.svg" summary="Authorize tools and agents with Zoho" - link="/references/auth-providers/zoho" + link="/en/references/auth-providers/zoho" category="Auth" /> <ToolCard name="Zoom" image="zoom_fav.svg" summary="Authorize tools and agents with Zoom" - link="/references/auth-providers/zoom" + link="/en/references/auth-providers/zoom" category="Auth" /> <ToolCard name="OAuth 2.0" image="oauth2" summary="Authorize tools and agents with any OAuth 2.0-compatible provider" - link="/references/auth-providers/oauth2" + link="/en/references/auth-providers/oauth2" category="Auth" /> </div> -If the auth provider you need is not listed, try the [OAuth 2.0](/references/auth-providers/oauth2) provider, or <ContactEmail domain="arcade.dev" user="contact">get in touch</ContactEmail> with us! +If the auth provider you need is not listed, try the [OAuth 2.0](/en/references/auth-providers/oauth2) provider, or <ContactEmail domain="arcade.dev" user="contact">get in touch</ContactEmail> with us! ## Using multiple providers of the same type @@ -292,4 +291,4 @@ async def list_emails( This is similar to the pattern used in the generic OAuth2 provider, but instead of using the `OAuth2` class, you use the `Google` class and specify the `id` of the auth provider you want to use. -See the docs about [Authoring Tools](/build/create-tools/tool-basics/build-mcp-server) for more information on how to create and serve a MCP Server. +See the docs about [Authoring Tools](/en/build/create-tools/tool-basics/build-mcp-server) for more information on how to create and serve a MCP Server. diff --git a/app/en/references/auth-providers/linear/page.mdx b/content/docs/references/auth-providers/linear.mdx similarity index 90% rename from app/en/references/auth-providers/linear/page.mdx rename to content/docs/references/auth-providers/linear.mdx index 7727df910..9a0a76bb6 100644 --- a/app/en/references/auth-providers/linear/page.mdx +++ b/content/docs/references/auth-providers/linear.mdx @@ -1,12 +1,12 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Linear +--- +title: Linear +--- The Linear auth provider enables tools and agents to call [Linear APIs](https://linear.app/developers/graphql) on behalf of a user. <Callout> Want to quickly get started with Linear in your agent or AI app? The pre-built - [Arcade Linear MCP Server](/resources/integrations/productivity/linear) is what you want! + [Arcade Linear MCP Server](/en/resources/integrations/productivity/linear) is what you want! </Callout> ### What's documented here @@ -15,7 +15,7 @@ This page describes how to use and configure Linear auth with Arcade. This auth provider is used by: -- The [Arcade Linear MCP Server](/resources/integrations/productivity/linear), which provides pre-built tools for interacting with Linear +- The [Arcade Linear MCP Server](/en/resources/integrations/productivity/linear), which provides pre-built tools for interacting with Linear - Your [app code](#using-linear-auth-in-app-code) that needs to call Linear APIs - Or, your [custom tools](#using-linear-auth-in-custom-tools) that need to call Linear APIs @@ -26,7 +26,7 @@ Arcade offers a default Linear auth provider that you can use in the Arcade Clou <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -81,14 +81,14 @@ Hit the **Create** button and the provider will be ready to be used. </Steps> -When you use tools that require Linear auth using your Arcade account credentials, Arcade will automatically use this Linear OAuth provider. If you have multiple Linear providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Linear auth using your Arcade account credentials, Arcade will automatically use this Linear OAuth provider. If you have multiple Linear providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. </Tabs.Tab> </Tabs> ## Using Linear auth in app code -Use the Linear auth provider in your own agents and AI apps to get a user token for Linear APIs. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Linear auth provider in your own agents and AI apps to get a user token for Linear APIs. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for Linear APIs: @@ -240,9 +240,9 @@ console.log(teams); ## Using Linear auth in custom tools -You can use the pre-built [Arcade Linear MCP Server](/resources/integrations/productivity/linear) to quickly build agents and AI apps that interact with Linear. +You can use the pre-built [Arcade Linear MCP Server](/en/resources/integrations/productivity/linear) to quickly build agents and AI apps that interact with Linear. -If the pre-built tools in the Linear MCP Server don't meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Linear API. +If the pre-built tools in the Linear MCP Server don't meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Linear API. Use the `Linear()` auth class to specify that a tool requires authorization with Linear. The `context.authorization.token` field will be automatically populated with the user's Linear token: diff --git a/app/en/references/auth-providers/linkedin/page.mdx b/content/docs/references/auth-providers/linkedin.mdx similarity index 91% rename from app/en/references/auth-providers/linkedin/page.mdx rename to content/docs/references/auth-providers/linkedin.mdx index 5e5f0f510..7b8eeb328 100644 --- a/app/en/references/auth-providers/linkedin/page.mdx +++ b/content/docs/references/auth-providers/linkedin.mdx @@ -1,12 +1,12 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# LinkedIn +--- +title: LinkedIn +--- The LinkedIn auth provider enables tools and agents to call the LinkedIn API on behalf of a user. <Callout> Want to quickly get started with LinkedIn in your agent or AI app? The pre-built - [Arcade LinkedIn MCP Server](/resources/integrations/social/linkedin) is what you want. + [Arcade LinkedIn MCP Server](/en/resources/integrations/social/linkedin) is what you want. </Callout> ### What's documented here @@ -15,7 +15,7 @@ This page describes how to use and configure LinkedIn auth with Arcade. This auth provider is used by: -- The [Arcade LinkedIn MCP Server](/resources/integrations/social/linkedin), which provides pre-built tools for interacting with LinkedIn +- The [Arcade LinkedIn MCP Server](/en/resources/integrations/social/linkedin), which provides pre-built tools for interacting with LinkedIn - Your [app code](#using-linkedin-auth-in-app-code) that needs to call LinkedIn APIs - Or, your [custom tools](#using-linkedin-auth-in-custom-tools) that need to call LinkedIn APIs @@ -26,7 +26,7 @@ Arcade offers a default LinkedIn auth provider that you can use in the Arcade Cl <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -80,14 +80,14 @@ Hit the **Create** button and the provider will be ready to be used. </Steps> -When you use tools that require LinkedIn auth using your Arcade account credentials, Arcade will automatically use this LinkedIn OAuth provider. If you have multiple LinkedIn providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require LinkedIn auth using your Arcade account credentials, Arcade will automatically use this LinkedIn OAuth provider. If you have multiple LinkedIn providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. </Tabs.Tab> </Tabs> ## Using LinkedIn auth in app code -Use the LinkedIn auth provider in your own agents and AI apps to get a user token for LinkedIn APIs. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the LinkedIn auth provider in your own agents and AI apps to get a user token for LinkedIn APIs. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for LinkedIn APIs: @@ -252,9 +252,9 @@ console.log(data); ## Using LinkedIn auth in custom tools -You can use the pre-built [Arcade LinkedIn MCP Server](/resources/integrations/social/linkedin) to quickly build agents and AI apps that interact with LinkedIn. +You can use the pre-built [Arcade LinkedIn MCP Server](/en/resources/integrations/social/linkedin) to quickly build agents and AI apps that interact with LinkedIn. -If the pre-built tools in the LinkedIn MCP Server don't meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with LinkedIn APIs. +If the pre-built tools in the LinkedIn MCP Server don't meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with LinkedIn APIs. Use the `LinkedIn()` auth class to specify that a tool requires authorization with LinkedIn. The `context.authorization.token` field will be automatically populated with the user's LinkedIn token: diff --git a/app/en/references/auth-providers/mailchimp/page.mdx b/content/docs/references/auth-providers/mailchimp.mdx similarity index 91% rename from app/en/references/auth-providers/mailchimp/page.mdx rename to content/docs/references/auth-providers/mailchimp.mdx index e88b1cf48..b9d1a1034 100644 --- a/app/en/references/auth-providers/mailchimp/page.mdx +++ b/content/docs/references/auth-providers/mailchimp.mdx @@ -1,12 +1,12 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Mailchimp +--- +title: Mailchimp +--- The Mailchimp auth provider enables tools and agents to call [Mailchimp Marketing APIs](https://mailchimp.com/developer/marketing/api/) on behalf of a user using OAuth 2.0 authentication. <Callout> Want to quickly get started with Mailchimp in your agent or AI app? The - pre-built [Arcade Mailchimp Marketing MCP Server](/resources/integrations/productivity/mailchimp-marketing-api) + pre-built [Arcade Mailchimp Marketing MCP Server](/en/resources/integrations/productivity/mailchimp-marketing-api) is what you want! </Callout> @@ -16,7 +16,7 @@ This page describes how to use and configure Mailchimp auth with Arcade. This auth provider is used by: -- The [Arcade Mailchimp Marketing MCP Server](/resources/integrations/productivity/mailchimp-marketing-api), which provides pre-built tools for interacting with Mailchimp +- The [Arcade Mailchimp Marketing MCP Server](/en/resources/integrations/productivity/mailchimp-marketing-api), which provides pre-built tools for interacting with Mailchimp - Your [app code](#using-mailchimp-auth-in-app-code) that needs to call the Mailchimp API - Or, your [custom tools](#using-mailchimp-auth-in-custom-tools) that need to call the Mailchimp API @@ -27,7 +27,7 @@ Arcade offers a default Mailchimp auth provider that you can use in the Arcade C <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -113,7 +113,7 @@ Hit the **Create** button and the provider will be ready to be used. <Callout type="info"> This method is only available when you are [self-hosting the Arcade - Engine](/operate/deploy/helm). + Engine](/en/operate/deploy/helm). </Callout> <Steps> @@ -157,11 +157,11 @@ Setting `provider_id: mailchimp` applies the Arcade Engine's built-in Mailchimp </Tabs.Tab> </Tabs> -When you use tools that require Mailchimp auth using your Arcade account credentials, Arcade will automatically use this Mailchimp OAuth provider. If you have multiple Mailchimp providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Mailchimp auth using your Arcade account credentials, Arcade will automatically use this Mailchimp OAuth provider. If you have multiple Mailchimp providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. ## Using Mailchimp auth in app code -Use the Mailchimp auth provider in your own agents and AI apps to get a user token for the Mailchimp API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Mailchimp auth provider in your own agents and AI apps to get a user token for the Mailchimp API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the Mailchimp API: @@ -270,9 +270,9 @@ const apiEndpoint = metadata.api_endpoint; ## Using Mailchimp auth in custom tools -You can use the pre-built [Arcade Mailchimp Marketing MCP Server](/resources/integrations/productivity/mailchimp-marketing-api) to quickly build agents and AI apps that interact with Mailchimp. +You can use the pre-built [Arcade Mailchimp Marketing MCP Server](/en/resources/integrations/productivity/mailchimp-marketing-api) to quickly build agents and AI apps that interact with Mailchimp. -If the pre-built tools in the Mailchimp MCP Server don't meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Mailchimp API. +If the pre-built tools in the Mailchimp MCP Server don't meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Mailchimp API. The Arcade MCP server SDK has no dedicated Mailchimp auth class. Use the `OAuth2()` auth class with the **ID** of the Mailchimp provider you configured in Arcade to specify that a tool requires authorization with Mailchimp. The `context.authorization.token` field will be automatically populated with the user's Mailchimp token: diff --git a/content/docs/references/auth-providers/meta.json b/content/docs/references/auth-providers/meta.json new file mode 100644 index 000000000..1a8a270f7 --- /dev/null +++ b/content/docs/references/auth-providers/meta.json @@ -0,0 +1,40 @@ +{ + "title": "Auth Providers", + "pages": [ + "index", + "oauth2", + "airtable", + "asana", + "atlassian", + "attio", + "calendly", + "cisco-duo", + "clickup", + "discord", + "dropbox", + "figma", + "github", + "google", + "hubspot", + "linear", + "linkedin", + "mailchimp", + "microsoft", + "microsoft-powerbi", + "miro", + "notion", + "pagerduty", + "reddit", + "salesforce", + "servicenow", + "slack", + "spotify", + "square", + "ticktick", + "twitch", + "x", + "zendesk", + "zoho", + "zoom" + ] +} diff --git a/app/en/references/auth-providers/microsoft-powerbi/page.mdx b/content/docs/references/auth-providers/microsoft-powerbi.mdx similarity index 92% rename from app/en/references/auth-providers/microsoft-powerbi/page.mdx rename to content/docs/references/auth-providers/microsoft-powerbi.mdx index 5e922125a..fe2c9ba00 100644 --- a/app/en/references/auth-providers/microsoft-powerbi/page.mdx +++ b/content/docs/references/auth-providers/microsoft-powerbi.mdx @@ -3,13 +3,9 @@ title: Microsoft Power BI description: Authorize tools and agents with the Power BI Service on behalf of a user --- -import { Tabs, Callout, Steps } from "nextra/components"; - -# Microsoft Power BI - <Callout type="info"> Microsoft Power BI is a **dedicated** auth provider, separate from the - general-purpose [Microsoft](/references/auth-providers/microsoft) provider. To + general-purpose [Microsoft](/en/references/auth-providers/microsoft) provider. To use it, add a **Microsoft Power BI** auth provider with your own Microsoft Entra (Azure AD) OAuth 2.0 credentials as described below. </Callout> @@ -24,7 +20,7 @@ This page describes how to use and configure Microsoft Power BI auth with Arcade This auth provider is used by: -- The Arcade [Power BI MCP server](/resources/integrations/productivity/microsoft-power-bi), which discovers workspaces, datasets, and reports; inspects and queries semantic models with DAX; and manages dataset refreshes +- The Arcade [Power BI MCP server](/en/resources/integrations/productivity/microsoft-power-bi), which discovers workspaces, datasets, and reports; inspects and queries semantic models with DAX; and manages dataset refreshes - Your [app code](#using-microsoft-power-bi-auth-in-app-code) that needs to call Power BI REST APIs - Or, your [custom tools](#using-microsoft-power-bi-auth-in-custom-tools) that need to call Power BI REST APIs @@ -33,7 +29,7 @@ This auth provider is used by: <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -107,14 +103,14 @@ Hit the **Create** button and the provider will be ready to be used. </Steps> -When you use tools that require Microsoft Power BI auth using your Arcade account credentials, Arcade will automatically use this provider. If you have multiple Microsoft Power BI providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Microsoft Power BI auth using your Arcade account credentials, Arcade will automatically use this provider. If you have multiple Microsoft Power BI providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. </Tabs.Tab> </Tabs> ## Using Microsoft Power BI auth in app code -Use the Microsoft Power BI auth provider in your own agents and AI apps to get a user token for the Power BI REST API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Microsoft Power BI auth provider in your own agents and AI apps to get a user token for the Power BI REST API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` with the `microsoft-powerbi` provider to get a user token for Power BI APIs: @@ -186,7 +182,7 @@ const token = authResponse.context.token; ## Using Microsoft Power BI auth in custom tools -You can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Power BI REST API. +You can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Power BI REST API. Use the `MicrosoftPowerBI()` auth class to specify that a tool requires authorization with the dedicated `microsoft-powerbi` provider. The `context.authorization.token` field will be automatically populated with the user's Power BI token: @@ -229,7 +225,7 @@ async def list_datasets( ### Why Power BI needs its own provider -The Power BI provider authorizes against the **Power BI Service** resource (`https://analysis.windows.net/powerbi/api`). It reuses Microsoft's Entra ID endpoints, but on its own connection, separate from the general-purpose [Microsoft](/references/auth-providers/microsoft) provider. +The Power BI provider authorizes against the **Power BI Service** resource (`https://analysis.windows.net/powerbi/api`). It reuses Microsoft's Entra ID endpoints, but on its own connection, separate from the general-purpose [Microsoft](/en/references/auth-providers/microsoft) provider. A Microsoft Entra access token is issued for a single resource (its _audience_). Power BI Service and Microsoft Graph are different resources, so a single authorize request cannot mix scopes from both: Entra rejects the sign-in with **AADSTS70011** (`The provided value for the input parameter scope is not valid`). Keeping Power BI on a dedicated provider guarantees every authorize request on that connection carries Power BI Service scopes only. diff --git a/app/en/references/auth-providers/microsoft/page.mdx b/content/docs/references/auth-providers/microsoft.mdx similarity index 75% rename from app/en/references/auth-providers/microsoft/page.mdx rename to content/docs/references/auth-providers/microsoft.mdx index 149a7be3f..a0ea9bf45 100644 --- a/app/en/references/auth-providers/microsoft/page.mdx +++ b/content/docs/references/auth-providers/microsoft.mdx @@ -1,6 +1,6 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Microsoft +--- +title: Microsoft +--- <Callout type="info"> Arcade Cloud provides a default Microsoft auth provider so you can try the @@ -29,7 +29,7 @@ This auth provider is used by: <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -76,17 +76,17 @@ Below is the list of scopes required by the Arcade Microsoft MCP servers: | MCP Server | Required Permissions | | ---------- | -------------------- | -| [Excel](/resources/integrations/productivity/microsoft-excel) | `Files.Read`<br/>`Files.ReadWrite`<br/>`User.Read` | -| [OneDrive](/resources/integrations/productivity/microsoft-onedrive) | `Files.Read`<br/>`Files.ReadWrite` | -| [Outlook Calendar](/resources/integrations/productivity/microsoft-outlook-calendar) | `Calendars.Read`<br/>`Calendars.ReadBasic`<br/>`Calendars.ReadWrite`<br/>`MailboxSettings.Read`<br/>`User.Read` | -| [Outlook Mail](/resources/integrations/productivity/microsoft-outlook-mail) | `Mail.Read`<br/>`Mail.Read.Shared`<br/>`Mail.ReadWrite`<br/>`Mail.ReadWrite.Shared`<br/>`Mail.Send`<br/>`Mail.Send.Shared`<br/>`MailboxSettings.Read`<br/>`User.Read` | -| [PowerPoint](/resources/integrations/productivity/microsoft-powerpoint) | `Files.Read`<br/>`Files.ReadWrite`<br/>`User.Read` | -| [SharePoint](/resources/integrations/productivity/microsoft-sharepoint) | `Files.ReadWrite`<br/>`Sites.Read.All`<br/>`Sites.ReadWrite.All`<br/>`User.Read` | -| [Teams](/resources/integrations/social/microsoft-teams) | `Calendars.Read`<br/>`Calendars.ReadWrite`<br/>`Channel.ReadBasic.All`<br/>`ChannelMember.Read.All`<br/>`ChannelMessage.Read.All`<br/>`ChannelMessage.ReadWrite`<br/>`ChannelMessage.Send`<br/>`Chat.Create`<br/>`Chat.Read`<br/>`Chat.ReadBasic`<br/>`Chat.ReadWrite`<br/>`ChatMember.ReadWrite`<br/>`ChatMessage.Read`<br/>`ChatMessage.Send`<br/>`Group.Read.All`<br/>`OnlineMeetingTranscript.Read.All`<br/>`OnlineMeetings.Read`<br/>`People.Read`<br/>`Team.ReadBasic.All`<br/>`TeamMember.Read.All`<br/>`User.Read`<br/>`User.ReadBasic.All` | -| [Users](/resources/integrations/productivity/microsoft-users) | `User.Read`<br/>`User.Read.All` | -| [Word](/resources/integrations/productivity/microsoft-word) | `Files.Read`<br/>`Files.ReadWrite`<br/>`User.Read` | - -The Arcade [Power BI MCP server](/resources/integrations/productivity/microsoft-power-bi) does not use this provider. It uses the separate [Microsoft Power BI](/references/auth-providers/microsoft-powerbi) auth provider. +| [Excel](/en/resources/integrations/productivity/microsoft-excel) | `Files.Read`<br/>`Files.ReadWrite`<br/>`User.Read` | +| [OneDrive](/en/resources/integrations/productivity/microsoft-onedrive) | `Files.Read`<br/>`Files.ReadWrite` | +| [Outlook Calendar](/en/resources/integrations/productivity/microsoft-outlook-calendar) | `Calendars.Read`<br/>`Calendars.ReadBasic`<br/>`Calendars.ReadWrite`<br/>`MailboxSettings.Read`<br/>`User.Read` | +| [Outlook Mail](/en/resources/integrations/productivity/microsoft-outlook-mail) | `Mail.Read`<br/>`Mail.Read.Shared`<br/>`Mail.ReadWrite`<br/>`Mail.ReadWrite.Shared`<br/>`Mail.Send`<br/>`Mail.Send.Shared`<br/>`MailboxSettings.Read`<br/>`User.Read` | +| [PowerPoint](/en/resources/integrations/productivity/microsoft-powerpoint) | `Files.Read`<br/>`Files.ReadWrite`<br/>`User.Read` | +| [SharePoint](/en/resources/integrations/productivity/microsoft-sharepoint) | `Files.ReadWrite`<br/>`Sites.Read.All`<br/>`Sites.ReadWrite.All`<br/>`User.Read` | +| [Teams](/en/resources/integrations/social/microsoft-teams) | `Calendars.Read`<br/>`Calendars.ReadWrite`<br/>`Channel.ReadBasic.All`<br/>`ChannelMember.Read.All`<br/>`ChannelMessage.Read.All`<br/>`ChannelMessage.ReadWrite`<br/>`ChannelMessage.Send`<br/>`Chat.Create`<br/>`Chat.Read`<br/>`Chat.ReadBasic`<br/>`Chat.ReadWrite`<br/>`ChatMember.ReadWrite`<br/>`ChatMessage.Read`<br/>`ChatMessage.Send`<br/>`Group.Read.All`<br/>`OnlineMeetingTranscript.Read.All`<br/>`OnlineMeetings.Read`<br/>`People.Read`<br/>`Team.ReadBasic.All`<br/>`TeamMember.Read.All`<br/>`User.Read`<br/>`User.ReadBasic.All` | +| [Users](/en/resources/integrations/productivity/microsoft-users) | `User.Read`<br/>`User.Read.All` | +| [Word](/en/resources/integrations/productivity/microsoft-word) | `Files.Read`<br/>`Files.ReadWrite`<br/>`User.Read` | + +The Arcade [Power BI MCP server](/en/resources/integrations/productivity/microsoft-power-bi) does not use this provider. It uses the separate [Microsoft Power BI](/en/references/auth-providers/microsoft-powerbi) auth provider. ## Configuring your own Microsoft Auth Provider in Arcade @@ -123,14 +123,14 @@ Hit the **Create** button and the provider will be ready to be used. </Steps> -When you use tools that require Microsoft auth using your Arcade account credentials, Arcade will automatically use this Microsoft OAuth provider. If you have multiple Microsoft providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Microsoft auth using your Arcade account credentials, Arcade will automatically use this Microsoft OAuth provider. If you have multiple Microsoft providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. </Tabs.Tab> </Tabs> ## Using Microsoft auth in app code -Use the Microsoft auth provider in your own agents and AI apps to get a user token for Microsoft Graph APIs. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Microsoft auth provider in your own agents and AI apps to get a user token for Microsoft Graph APIs. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for Microsoft Graph APIs: @@ -196,7 +196,7 @@ const token = authResponse.context.token; ## Using Microsoft auth in custom tools -You can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with Microsoft Graph APIs. +You can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with Microsoft Graph APIs. Use the `Microsoft()` auth class to specify that a tool requires authorization with Microsoft. The `context.authorization.token` field will be automatically populated with the user's Microsoft token: diff --git a/app/en/references/auth-providers/miro/page.mdx b/content/docs/references/auth-providers/miro.mdx similarity index 90% rename from app/en/references/auth-providers/miro/page.mdx rename to content/docs/references/auth-providers/miro.mdx index 448f6c90b..3300f5968 100644 --- a/app/en/references/auth-providers/miro/page.mdx +++ b/content/docs/references/auth-providers/miro.mdx @@ -1,12 +1,12 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Miro +--- +title: Miro +--- The Miro auth provider enables tools and agents to call [Miro APIs](https://developers.miro.com/reference/api-reference) on behalf of a user using OAuth 2.0 authentication. <Callout> Want to quickly get started with Miro in your agent or AI app? The pre-built - [Arcade Miro MCP Server](/resources/integrations/productivity/miro-api) is what + [Arcade Miro MCP Server](/en/resources/integrations/productivity/miro-api) is what you want! </Callout> @@ -16,7 +16,7 @@ This page describes how to use and configure Miro auth with Arcade. This auth provider is used by: -- The [Arcade Miro MCP Server](/resources/integrations/productivity/miro-api), which provides pre-built tools for interacting with Miro +- The [Arcade Miro MCP Server](/en/resources/integrations/productivity/miro-api), which provides pre-built tools for interacting with Miro - Your [app code](#using-miro-auth-in-app-code) that needs to call the Miro API - Or, your [custom tools](#using-miro-auth-in-custom-tools) that need to call the Miro API @@ -27,7 +27,7 @@ Arcade offers a default Miro auth provider that you can use in the Arcade Cloud <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -116,7 +116,7 @@ Hit the **Create** button and the provider will be ready to be used. <Callout type="info"> This method is only available when you are [self-hosting the Arcade - Engine](/operate/deploy/helm). + Engine](/en/operate/deploy/helm). </Callout> <Steps> @@ -160,11 +160,11 @@ Setting `provider_id: miro` applies the Arcade Engine's built-in Miro configurat </Tabs.Tab> </Tabs> -When you use tools that require Miro auth using your Arcade account credentials, Arcade will automatically use this Miro OAuth provider. If you have multiple Miro providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Miro auth using your Arcade account credentials, Arcade will automatically use this Miro OAuth provider. If you have multiple Miro providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. ## Using Miro auth in app code -Use the Miro auth provider in your own agents and AI apps to get a user token for the Miro API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Miro auth provider in your own agents and AI apps to get a user token for the Miro API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the Miro API: @@ -230,9 +230,9 @@ const token = authResponse.context.token; ## Using Miro auth in custom tools -You can use the pre-built [Arcade Miro MCP Server](/resources/integrations/productivity/miro-api) to quickly build agents and AI apps that interact with Miro. +You can use the pre-built [Arcade Miro MCP Server](/en/resources/integrations/productivity/miro-api) to quickly build agents and AI apps that interact with Miro. -If the pre-built tools in the Miro MCP Server don't meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Miro API. +If the pre-built tools in the Miro MCP Server don't meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Miro API. The Arcade MCP server SDK has no dedicated Miro auth class. Use the `OAuth2()` auth class with the **ID** of the Miro provider you configured in Arcade to specify that a tool requires authorization with Miro. The `context.authorization.token` field will be automatically populated with the user's Miro token: diff --git a/app/en/references/auth-providers/notion/page.mdx b/content/docs/references/auth-providers/notion.mdx similarity index 89% rename from app/en/references/auth-providers/notion/page.mdx rename to content/docs/references/auth-providers/notion.mdx index 374021e60..4b0bf0669 100644 --- a/app/en/references/auth-providers/notion/page.mdx +++ b/content/docs/references/auth-providers/notion.mdx @@ -1,6 +1,6 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Notion +--- +title: Notion +--- The Notion auth provider enables tools and agents to call [Notion APIs](https://developers.notion.com/reference/intro) on behalf of a user. @@ -18,7 +18,7 @@ This auth provider is used by: <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -68,14 +68,14 @@ Hit the **Create** button and the provider will be ready to be used. </Steps> -When you use tools that require Notion auth using your Arcade account credentials, Arcade will automatically use this Notion OAuth provider. If you have multiple Notion providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Notion auth using your Arcade account credentials, Arcade will automatically use this Notion OAuth provider. If you have multiple Notion providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. </Tabs.Tab> </Tabs> ## Using Notion auth in app code -Use the Notion auth provider in your own agents and AI apps to get a user token for the Notion API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Notion auth provider in your own agents and AI apps to get a user token for the Notion API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the Notion API: @@ -138,9 +138,9 @@ const token = authResponse.context.token; ## Using Notion auth in custom tools -You can use the pre-built [Arcade Notion MCP Server](/resources/integrations/productivity/notion) to quickly build agents and AI apps that interact with Notion. +You can use the pre-built [Arcade Notion MCP Server](/en/resources/integrations/productivity/notion) to quickly build agents and AI apps that interact with Notion. -If the pre-built tools in the Notion MCP Server don't meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Notion API. +If the pre-built tools in the Notion MCP Server don't meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Notion API. Use the `Notion()` auth class to specify that a tool requires authorization with Notion. The `context.authorization.token` field will be automatically populated with the user's Notion token: diff --git a/app/en/references/auth-providers/oauth2/page.mdx b/content/docs/references/auth-providers/oauth2.mdx similarity index 96% rename from app/en/references/auth-providers/oauth2/page.mdx rename to content/docs/references/auth-providers/oauth2.mdx index 6e6f9cefe..a4ef4be09 100644 --- a/app/en/references/auth-providers/oauth2/page.mdx +++ b/content/docs/references/auth-providers/oauth2.mdx @@ -3,16 +3,13 @@ title: OAuth 2.0 description: Authorize tools and agents with any OAuth 2.0-compatible provider --- -import { Tabs, Callout, Steps } from "nextra/components"; -import ToggleContent from "@/app/_components/toggle-content"; -import { ContactEmail } from "@/app/_components/contact-email"; - -# OAuth 2.0 +import ToggleContent from "@/components/toggle-content"; +import { ContactEmail } from "@/components/contact-email"; The OAuth 2.0 auth provider enables tools and agents to authorize with any OAuth 2.0-compatible API on behalf of a user. <Callout> - Arcade has [pre-built auth providers](/references/auth-providers) for many popular + Arcade has [pre-built auth providers](/en/references/auth-providers) for many popular OAuth 2.0 services. Use this OAuth 2.0 provider to connect to other systems that aren't pre-built. </Callout> @@ -30,7 +27,7 @@ The only supported OAuth 2.0 flow is the authorization code grant flow (with or ## Configuring OAuth 2.0 -How you configure the OAuth 2.0 provider depends on whether you use the Arcade Cloud Engine or a [self-hosted Engine](/operate/deploy/helm). If you use the Cloud Engine, you must configure your provider in the Dashboard. +How you configure the OAuth 2.0 provider depends on whether you use the Arcade Cloud Engine or a [self-hosted Engine](/en/operate/deploy/helm). If you use the Cloud Engine, you must configure your provider in the Dashboard. <Callout type="info"> When configuring your app in the OAuth 2.0 enabled service, you must use the @@ -43,7 +40,7 @@ How you configure the OAuth 2.0 provider depends on whether you use the Arcade C <Callout type="info"> When using the Arcade Cloud Platform, the Dashboard is available at [`https://api.arcade.dev/dashboard`](https://api.arcade.dev/dashboard). If you - are [self-hosting Arcade](/operate/deploy/helm), by default + are [self-hosting Arcade](/en/operate/deploy/helm), by default the Dashboard is available at [`http://localhost:9099/dashboard`](http://localhost:9099/dashboard). Adjust the host and port, if necessary, to match your environment. @@ -65,7 +62,7 @@ When you use tools that require OAuth 2.0 authorization using your Arcade accoun <Callout type="info"> This method is only available when you are [self-hosting the - engine](/operate/deploy/helm). + engine](/en/operate/deploy/helm). </Callout> <Steps> @@ -87,7 +84,7 @@ HOOLI_CLIENT_SECRET="<your client secret>" ``` <Callout> - See [configuration](/operate/deploy/helm#configuration) for more + See [configuration](/en/operate/deploy/helm#configuration) for more information on how to set environment variables and configure the Arcade Engine. </Callout> @@ -97,7 +94,7 @@ HOOLI_CLIENT_SECRET="<your client secret>" <Callout> To locate the `engine.yaml` file in your OS after installing the Arcade Engine, check the [Engine configuration - file](/operate/deploy/helm#configuration) + file](/en/operate/deploy/helm#configuration) documentation. </Callout> @@ -542,7 +539,7 @@ auth: ## Using OAuth 2.0 in app code -Use the OAuth 2.0 auth provider in your own agents and AI apps to get a user token for any OAuth 2.0-compatible APIs. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the OAuth 2.0 auth provider in your own agents and AI apps to get a user token for any OAuth 2.0-compatible APIs. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get an access token: @@ -609,7 +606,7 @@ const token = authResponse.context.token; ## Using OAuth 2.0 in custom tools -You can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with any OAuth 2.0-compatible APIs. +You can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with any OAuth 2.0-compatible APIs. Use the `OAuth2()` auth class to specify that a tool requires OAuth 2.0 authorization. In your tool function, `context.authorization` will be automatically populated with the following properties: diff --git a/app/en/references/auth-providers/pagerduty/page.mdx b/content/docs/references/auth-providers/pagerduty.mdx similarity index 90% rename from app/en/references/auth-providers/pagerduty/page.mdx rename to content/docs/references/auth-providers/pagerduty.mdx index 2ea0b8122..421f6d457 100644 --- a/app/en/references/auth-providers/pagerduty/page.mdx +++ b/content/docs/references/auth-providers/pagerduty.mdx @@ -1,12 +1,12 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# PagerDuty +--- +title: PagerDuty +--- The PagerDuty auth provider enables tools and agents to call [PagerDuty APIs](https://docs.pagerduty.com/developer/api/reference) on behalf of a user using OAuth 2.0 authentication. <Callout type="warning"> The [Arcade PagerDuty MCP - server](/resources/integrations/development/pagerduty) requests granular + server](/en/resources/integrations/development/pagerduty) requests granular PagerDuty scopes, so register a **Scoped OAuth** app and grant it the scopes listed under [Permissions](#permissions). See the [PagerDuty OAuth documentation](https://docs.pagerduty.com/developer/oauth-functionality). @@ -15,7 +15,7 @@ The PagerDuty auth provider enables tools and agents to call [PagerDuty APIs](ht <Callout> Want to quickly get started with PagerDuty in your agent or AI app? The pre-built [Arcade PagerDuty MCP - Server](/resources/integrations/development/pagerduty) is what you want! + Server](/en/resources/integrations/development/pagerduty) is what you want! </Callout> ### What's documented here @@ -24,7 +24,7 @@ This page describes how to use and configure PagerDuty auth with Arcade. This auth provider is used by: -- The [Arcade PagerDuty MCP Server](/resources/integrations/development/pagerduty), which provides pre-built tools for interacting with PagerDuty +- The [Arcade PagerDuty MCP Server](/en/resources/integrations/development/pagerduty), which provides pre-built tools for interacting with PagerDuty - Your [app code](#using-pagerduty-auth-in-app-code) that needs to call the PagerDuty API - Or, your [custom tools](#using-pagerduty-auth-in-custom-tools) that need to call the PagerDuty API @@ -33,7 +33,7 @@ This auth provider is used by: <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -124,7 +124,7 @@ Hit the **Create** button and the provider will be ready to be used. <Callout type="info"> This method is only available when you are [self-hosting the - engine](/operate/deploy/helm) + engine](/en/operate/deploy/helm) </Callout> <Steps> @@ -166,11 +166,11 @@ auth: </Tabs.Tab> </Tabs> -When you use tools that require PagerDuty auth using your Arcade account credentials, Arcade will automatically use this PagerDuty OAuth provider. If you have multiple PagerDuty providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require PagerDuty auth using your Arcade account credentials, Arcade will automatically use this PagerDuty OAuth provider. If you have multiple PagerDuty providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. ## Using PagerDuty auth in app code -Use the PagerDuty auth provider in your own agents and AI apps to get a user token for the PagerDuty API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the PagerDuty auth provider in your own agents and AI apps to get a user token for the PagerDuty API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the PagerDuty API: @@ -236,9 +236,9 @@ const token = authResponse.context.token; ## Using PagerDuty auth in custom tools -You can use the pre-built [Arcade PagerDuty MCP Server](/resources/integrations/development/pagerduty) to quickly build agents and AI apps that interact with PagerDuty. +You can use the pre-built [Arcade PagerDuty MCP Server](/en/resources/integrations/development/pagerduty) to quickly build agents and AI apps that interact with PagerDuty. -If the pre-built tools in the PagerDuty MCP Server don't meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the PagerDuty API. +If the pre-built tools in the PagerDuty MCP Server don't meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the PagerDuty API. Use the `PagerDuty()` auth class to specify that a tool requires authorization with PagerDuty. The `context.authorization.token` field will be automatically populated with the user's PagerDuty token: diff --git a/app/en/references/auth-providers/reddit/page.mdx b/content/docs/references/auth-providers/reddit.mdx similarity index 89% rename from app/en/references/auth-providers/reddit/page.mdx rename to content/docs/references/auth-providers/reddit.mdx index f9b28e055..c469411d0 100644 --- a/app/en/references/auth-providers/reddit/page.mdx +++ b/content/docs/references/auth-providers/reddit.mdx @@ -1,6 +1,6 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Reddit +--- +title: Reddit +--- <Callout type="info"> Arcade Cloud includes a default Reddit auth provider, so you can start @@ -18,7 +18,7 @@ This page describes how to use and configure Reddit auth with Arcade. This auth provider is used by: -- The [Arcade Reddit MCP server](/resources/integrations/social/reddit), which requests the `history`, `identity`, `read`, and `submit` scopes +- The [Arcade Reddit MCP server](/en/resources/integrations/social/reddit), which requests the `history`, `identity`, `read`, and `submit` scopes - Your [app code](#using-reddit-auth-in-app-code) that needs to call Reddit APIs - Or, your [custom tools](#using-reddit-auth-in-custom-tools) that need to call Reddit APIs @@ -27,7 +27,7 @@ This auth provider is used by: <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -79,14 +79,14 @@ Hit the **Create** button and the provider will be ready to be used. </Steps> -When you use tools that require Reddit auth using your Arcade account credentials, Arcade will automatically use this Reddit OAuth provider. If you have multiple Reddit providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Reddit auth using your Arcade account credentials, Arcade will automatically use this Reddit OAuth provider. If you have multiple Reddit providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. </Tabs.Tab> </Tabs> ## Using Reddit auth in app code -Use the Reddit auth provider in your own agents and AI apps to get a user-scoped token for the Reddit API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Reddit auth provider in your own agents and AI apps to get a user-scoped token for the Reddit API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the Reddit API: @@ -152,7 +152,7 @@ const token = authResponse.context.token; ## Using Reddit auth in custom tools -You can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Reddit API. +You can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Reddit API. Use the `Reddit()` auth class to specify that a tool requires authorization with Reddit. The `context.authorization.token` field will be automatically populated with the user's Reddit token: diff --git a/app/en/references/auth-providers/salesforce/page.mdx b/content/docs/references/auth-providers/salesforce.mdx similarity index 91% rename from app/en/references/auth-providers/salesforce/page.mdx rename to content/docs/references/auth-providers/salesforce.mdx index a2e82eb59..14aa86de8 100644 --- a/app/en/references/auth-providers/salesforce/page.mdx +++ b/content/docs/references/auth-providers/salesforce.mdx @@ -1,8 +1,9 @@ -import { Tabs, Callout, Steps } from "nextra/components"; -import ToggleContent from "@/app/_components/toggle-content"; -import { SignupLink } from "@/app/_components/analytics"; +--- +title: Salesforce +--- -# Salesforce +import ToggleContent from "@/components/toggle-content"; +import { SignupLink } from "@/components/analytics"; The Salesforce auth provider enables tools and agents to call Salesforce APIs on behalf of a user. @@ -12,7 +13,7 @@ This page describes how to use and configure Salesforce auth with Arcade. This auth provider is used by: -- The [Arcade Salesforce MCP Server](/resources/integrations/sales/salesforce), which provides pre-built tools for interacting with Salesforce services +- The [Arcade Salesforce MCP Server](/en/resources/integrations/sales/salesforce), which provides pre-built tools for interacting with Salesforce services - Your [app code](#calling-salesforce-apis-directly) that needs to call Salesforce APIs - Or, your [custom tools](#create-your-own-salesforce-tools) that need to call Salesforce APIs @@ -21,7 +22,7 @@ This auth provider is used by: <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -94,7 +95,7 @@ Set the `SALESFORCE_ORG_SUBDOMAIN` secret in the Arcade Dashboard: The Salesforce API requires the App developer to create [OAuth custom scopes](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_customscopes.htm&type=5) defining granular permissions for their application users to authorize. -The custom scopes required by the [Arcade Salesforce MCP Server](/resources/integrations/sales/salesforce) are listed below, along with their descriptions: +The custom scopes required by the [Arcade Salesforce MCP Server](/en/resources/integrations/sales/salesforce) are listed below, along with their descriptions: - `read_account`: Read access to account data. - `read_contact`: Read access to contact data. @@ -111,7 +112,7 @@ The custom scopes required by the [Arcade Salesforce MCP Server](/resources/inte Follow the [Create an OAuth Custom Scope](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_customscopes_create.htm&type=5) and [Assign an OAuth Custom Scope to an External Client App](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_customscopes_assign.htm&type=5) Salesforce documentation to understand how to define and assign these scopes to your Salesforce app. <Callout type="info"> - If you use the [Arcade Salesforce MCP Server](/resources/integrations/sales/salesforce), you must create, assign, and request every custom scope in the list above. Salesforce requires at least one custom scope on the app. One scope is not enough for the MCP server. + If you use the [Arcade Salesforce MCP Server](/en/resources/integrations/sales/salesforce), you must create, assign, and request every custom scope in the list above. Salesforce requires at least one custom scope on the app. One scope is not enough for the MCP server. </Callout> <Callout type="info"> @@ -119,7 +120,7 @@ Follow the [Create an OAuth Custom Scope](https://help.salesforce.com/s/articleV </Callout> <Callout> - If you're creating your own [custom Salesforce tools](/build/create-tools/tool-basics/build-mcp-server) or using Arcade to authorize users and call Salesforce APIs directly, you are free to define custom scopes that fit your application. You must assign at least one custom scope to your Salesforce app. + If you're creating your own [custom Salesforce tools](/en/build/create-tools/tool-basics/build-mcp-server) or using Arcade to authorize users and call Salesforce APIs directly, you are free to define custom scopes that fit your application. You must assign at least one custom scope to your Salesforce app. For custom tools, the scope names aren't attached to any endpoint or action. Honor the permissions you communicate to the user when they authorize the app. You could assign one single scope (e.g. `fullaccess`) and use it to query any Salesforce API endpoint. </Callout> @@ -145,7 +146,7 @@ Go to the [Arcade Dashboard](https://api.arcade.dev/dashboard) and log in with y #### Enter the provider details -- Enter `salesforce` as the **ID** for your provider (the ID must be `salesforce` to use the [Arcade Salesforce MCP Server](/resources/integrations/sales/salesforce)). +- Enter `salesforce` as the **ID** for your provider (the ID must be `salesforce` to use the [Arcade Salesforce MCP Server](/en/resources/integrations/sales/salesforce)). - Optionally enter a **Description**. - Enter your **Client ID** (Consumer Key) and **Client Secret** (Consumer Secret) from your Salesforce External Client App. - Note the **Redirect URI** generated by Arcade. This must be set as your Salesforce External Client App's callback URL. @@ -194,23 +195,23 @@ Click the **Create** button and the provider will be ready to be used in the Arc ## Using the Arcade Salesforce MCP Server -The [Arcade Salesforce MCP Server](/resources/integrations/sales/salesforce) provides tools to interact with various Salesforce objects, such as accounts, contacts, leads, opportunities, notes, tasks, email messages, call logs, etc. +The [Arcade Salesforce MCP Server](/en/resources/integrations/sales/salesforce) provides tools to interact with various Salesforce objects, such as accounts, contacts, leads, opportunities, notes, tasks, email messages, call logs, etc. -Refer to the [MCP Server documentation and examples](/resources/integrations/sales/salesforce) to learn how to use the MCP Server to build agents and AI apps that interact with Salesforce services. +Refer to the [MCP Server documentation and examples](/en/resources/integrations/sales/salesforce) to learn how to use the MCP Server to build agents and AI apps that interact with Salesforce services. <Callout> Check our introductory documentation to understand what are tools and how - [tool calling works](/build/tool-calling). + [tool calling works](/en/build/tool-calling). </Callout> ## Calling Salesforce APIs directly -Use the Salesforce auth provider to get a user authorization token and call Salesforce API endpoints directly, without the use of any tools. See [How Arcade helps with Agent Authorization](/get-started/about-arcade) to understand how this works. +Use the Salesforce auth provider to get a user authorization token and call Salesforce API endpoints directly, without the use of any tools. See [How Arcade helps with Agent Authorization](/en/get-started/about-arcade) to understand how this works. ### Prerequisites 1. Create an <SignupLink linkLocation="docs:/home/auth-providers/salesforce">Arcade account</SignupLink> -1. Get an [Arcade API key](/get-started/setup/api-keys). +1. Get an [Arcade API key](/en/get-started/setup/api-keys). 1. Set the `ARCADE_API_KEY` environment variable (`export ARCADE_API_KEY=<your-api-key>` on Bash, `$env:ARCADE_API_KEY="<your-api-key>"` on PowerShell). 1. Make sure to have Python 3.10+ or Node.js 18+ installed. @@ -615,7 +616,7 @@ console.log(await response.json()); ## Create your own Salesforce Tools -If the pre-built tools in the [Arcade Salesforce MCP Server](/resources/integrations/sales/salesforce) don't meet your needs, you can create your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Salesforce APIs. +If the pre-built tools in the [Arcade Salesforce MCP Server](/en/resources/integrations/sales/salesforce) don't meet your needs, you can create your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Salesforce APIs. The code implemented in the Arcade Salesforce tools is the best guide for you to understand how to implement your own. ``` diff --git a/app/en/references/auth-providers/servicenow/page.mdx b/content/docs/references/auth-providers/servicenow.mdx similarity index 88% rename from app/en/references/auth-providers/servicenow/page.mdx rename to content/docs/references/auth-providers/servicenow.mdx index 91198e1c9..6757e76ce 100644 --- a/app/en/references/auth-providers/servicenow/page.mdx +++ b/content/docs/references/auth-providers/servicenow.mdx @@ -1,12 +1,9 @@ --- -title: "ServiceNow" -description: "Configure a ServiceNow auth provider in Arcade to authorize tools and agents against a ServiceNow instance" +title: ServiceNow +description: Configure a ServiceNow auth provider in Arcade to authorize tools and agents against a ServiceNow instance --- -import { Tabs, Callout, Steps } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; - -# ServiceNow +import { SignupLink } from "@/components/analytics"; The ServiceNow auth provider enables tools and agents to call ServiceNow APIs on behalf of a user. @@ -16,14 +13,14 @@ This page describes how to use and configure ServiceNow auth with Arcade. This auth provider is used by: -- The [Arcade ServiceNow MCP Server](/resources/integrations/customer-support/servicenow), which provides pre-built tools for incidents, changes, catalog requests, knowledge articles, and the CMDB +- The [Arcade ServiceNow MCP Server](/en/resources/integrations/customer-support/servicenow), which provides pre-built tools for incidents, changes, catalog requests, knowledge articles, and the CMDB - Your [app code](#calling-servicenow-apis-directly) that needs to call ServiceNow APIs - Or, your [custom tools](#using-servicenow-auth-in-custom-tools) that need to call ServiceNow APIs <Callout type="info"> Arcade does not offer a default ServiceNow auth provider. To use ServiceNow auth, you must create a [custom provider - configuration](/references/auth-providers/oauth2) as described below. You + configuration](/en/references/auth-providers/oauth2) as described below. You create the OAuth client in your ServiceNow instance, then register it in Arcade as an OAuth 2.0 provider with the ID `servicenow`. </Callout> @@ -166,7 +163,7 @@ Go to the [Arcade Dashboard](https://api.arcade.dev/dashboard) and log in with y #### Enter the provider details -- Enter `servicenow` as the **ID** for your provider. The ID must be `servicenow` for the [Arcade ServiceNow MCP Server](/resources/integrations/customer-support/servicenow) to resolve it. +- Enter `servicenow` as the **ID** for your provider. The ID must be `servicenow` for the [Arcade ServiceNow MCP Server](/en/resources/integrations/customer-support/servicenow) to resolve it. - Optionally enter a **Description**. - Enter the **Client ID** and **Client Secret** you copied from ServiceNow. - Copy the **Redirect URI** generated by Arcade. You'll add it to your ServiceNow integration after you create the provider. @@ -203,23 +200,23 @@ Go back to the ServiceNow integration record you created earlier, paste the Redi ## Using the Arcade ServiceNow MCP server -The [Arcade ServiceNow MCP Server](/resources/integrations/customer-support/servicenow) provides tools to search and update work records, order catalog items, read knowledge articles, and explore the CMDB. +The [Arcade ServiceNow MCP Server](/en/resources/integrations/customer-support/servicenow) provides tools to search and update work records, order catalog items, read knowledge articles, and explore the CMDB. Every call runs as the authorizing ServiceNow user and honors that user's roles and ACLs, so a tool returns only what that user could see or change in the ServiceNow UI. <Callout> Check our introductory documentation to understand what are tools and how - [tool calling works](/build/tool-calling). + [tool calling works](/en/build/tool-calling). </Callout> ## Calling ServiceNow APIs directly -Use the ServiceNow auth provider to get a user authorization token and call ServiceNow API endpoints directly, without the use of any tools. See [How Arcade helps with Agent Authorization](/get-started/about-arcade) to understand how this works. +Use the ServiceNow auth provider to get a user authorization token and call ServiceNow API endpoints directly, without the use of any tools. See [How Arcade helps with Agent Authorization](/en/get-started/about-arcade) to understand how this works. ### Prerequisites 1. Create an <SignupLink linkLocation="docs:/home/auth-providers/servicenow">Arcade account</SignupLink> -1. Get an [Arcade API key](/get-started/setup/api-keys). +1. Get an [Arcade API key](/en/get-started/setup/api-keys). 1. Set the `ARCADE_API_KEY` environment variable (`export ARCADE_API_KEY=your-api-key` on Bash, `$env:ARCADE_API_KEY="your-api-key"` on PowerShell). 1. Make sure to have Python 3.10+ or Node.js 18+ installed. @@ -291,7 +288,7 @@ console.log(await response.json()); ## Using ServiceNow auth in custom tools -If the [Arcade ServiceNow MCP Server](/resources/integrations/customer-support/servicenow) does not meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with ServiceNow APIs. +If the [Arcade ServiceNow MCP Server](/en/resources/integrations/customer-support/servicenow) does not meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with ServiceNow APIs. Use the `OAuth2()` auth class to specify that a tool requires authorization with ServiceNow. The `id` must match the ID of the provider you created, and `context.get_auth_token_or_empty()` returns the user's ServiceNow token: @@ -343,5 +340,5 @@ async def get_active_incidents( ## Next steps -- Explore the [Arcade ServiceNow MCP Server](/resources/integrations/customer-support/servicenow) tool reference. -- Connecting to a ServiceNow-hosted MCP server instead? See [Connect a ServiceNow Hosted MCP Server](/operate/governance/remote-mcp-servers/servicenow). +- Explore the [Arcade ServiceNow MCP Server](/en/resources/integrations/customer-support/servicenow) tool reference. +- Connecting to a ServiceNow-hosted MCP server instead? See [Connect a ServiceNow Hosted MCP Server](/en/operate/governance/remote-mcp-servers/servicenow). diff --git a/app/en/references/auth-providers/slack/page.mdx b/content/docs/references/auth-providers/slack.mdx similarity index 85% rename from app/en/references/auth-providers/slack/page.mdx rename to content/docs/references/auth-providers/slack.mdx index e51f53713..b6b1a54af 100644 --- a/app/en/references/auth-providers/slack/page.mdx +++ b/content/docs/references/auth-providers/slack.mdx @@ -1,12 +1,12 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Slack +--- +title: Slack +--- The Slack auth provider enables tools and agents to call [Slack APIs](https://api.slack.com/docs) on behalf of a user. <Callout> Want to quickly get started with Slack in your agent or AI app? The pre-built - [Arcade Slack MCP Server](/resources/integrations/social/slack) is what you + [Arcade Slack MCP Server](/en/resources/integrations/social/slack) is what you want! </Callout> @@ -16,7 +16,7 @@ This page describes how to use and configure Slack auth with Arcade. This auth provider is used by: -- The [Arcade Slack MCP Server](/resources/integrations/social/slack), which provides pre-built tools for interacting with Slack +- The [Arcade Slack MCP Server](/en/resources/integrations/social/slack), which provides pre-built tools for interacting with Slack - Your [app code](#using-slack-auth-in-app-code) that needs to call the Slack API - Or, your [custom tools](#using-slack-auth-in-custom-tools) that need to call the Slack API @@ -25,7 +25,7 @@ This auth provider is used by: <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -40,12 +40,12 @@ Before showing how to configure your Slack app credentials, let's go through the <Callout type="warning"> On May 29, 2025, [Slack announced](https://api.slack.com/changelog/2025-05-terms-rate-limit-update-and-faq) changes to their API rate-limits and terms of service for apps that are not approved for the Slack Marketplace. -The `conversations.history` and `conversations.replies` endpoints are now limited to 1 request/minute and up to 15 objects returned per request. This affects various tools in the [Arcade Slack MCP Server](/resources/integrations/social/slack). Additionally, the [API Terms of Service](https://slack.com/terms-of-service/api) now requires [Slack Marketplace](https://api.slack.com/slack-marketplace/using) approval for commercial distribution. +The `conversations.history` and `conversations.replies` endpoints are now limited to 1 request/minute and up to 15 objects returned per request. This affects various tools in the [Arcade Slack MCP Server](/en/resources/integrations/social/slack). Additionally, the [API Terms of Service](https://slack.com/terms-of-service/api) now requires [Slack Marketplace](https://api.slack.com/slack-marketplace/using) approval for commercial distribution. </Callout> - Follow Slack's guide to [registering a Slack app](https://api.slack.com/quickstart) -- If you plan to use the [Arcade Slack MCP Server](/resources/integrations/social/slack), select the scopes below (include additional scopes for your application's authorization needs or custom tools, if any): +- If you plan to use the [Arcade Slack MCP Server](/en/resources/integrations/social/slack), select the scopes below (include additional scopes for your application's authorization needs or custom tools, if any): - `channels:history` - `channels:read` - `channels:write.invites` @@ -102,14 +102,14 @@ Hit the **Create** button and the provider will be ready to be used. </Steps> -When you use tools that require Slack auth using your Arcade account credentials, Arcade will automatically use this Slack OAuth provider. If you have multiple Slack providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Slack auth using your Arcade account credentials, Arcade will automatically use this Slack OAuth provider. If you have multiple Slack providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. </Tabs.Tab> </Tabs> ## Using Slack auth in app code -Use the Slack auth provider in your own agents and AI apps to get a user token for the Slack API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Slack auth provider in your own agents and AI apps to get a user token for the Slack API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the Slack API: @@ -180,9 +180,9 @@ const token = authResponse.context.token; ## Using Slack auth in custom tools -You can use the pre-built [Arcade Slack MCP Server](/resources/integrations/social/slack) to quickly build agents and AI apps that interact with Slack. +You can use the pre-built [Arcade Slack MCP Server](/en/resources/integrations/social/slack) to quickly build agents and AI apps that interact with Slack. -If the pre-built tools in the Slack MCP Server don't meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Slack API. +If the pre-built tools in the Slack MCP Server don't meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Slack API. Use the `Slack()` auth class to specify that a tool requires authorization with Slack. The `context.authorization.token` field will be automatically populated with the user's Slack token: diff --git a/app/en/references/auth-providers/spotify/page.mdx b/content/docs/references/auth-providers/spotify.mdx similarity index 88% rename from app/en/references/auth-providers/spotify/page.mdx rename to content/docs/references/auth-providers/spotify.mdx index 555b2d0ae..a86eb16c4 100644 --- a/app/en/references/auth-providers/spotify/page.mdx +++ b/content/docs/references/auth-providers/spotify.mdx @@ -1,6 +1,6 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Spotify +--- +title: Spotify +--- <Callout type="info"> At this time, Arcade does not offer a default Spotify auth provider. To use @@ -16,7 +16,7 @@ This page describes how to use and configure Spotify auth with Arcade. This auth provider is used by: -- The [Arcade Spotify MCP Server](/resources/integrations/entertainment/spotify), which provides pre-built tools for interacting with Spotify +- The [Arcade Spotify MCP Server](/en/resources/integrations/entertainment/spotify), which provides pre-built tools for interacting with Spotify - Your [app code](#using-spotify-auth-in-app-code) that needs to call Spotify APIs - Or, your [custom tools](#using-spotify-auth-in-custom-tools) that need to call Spotify APIs @@ -25,7 +25,7 @@ This auth provider is used by: <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -78,14 +78,14 @@ Hit the **Create** button and the provider will be ready to be used. </Steps> -When you use tools that require Spotify auth using your Arcade account credentials, Arcade will automatically use this Spotify OAuth provider. If you have multiple Spotify providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Spotify auth using your Arcade account credentials, Arcade will automatically use this Spotify OAuth provider. If you have multiple Spotify providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. </Tabs.Tab> </Tabs> ## Using Spotify auth in app code -Use the Spotify auth provider in your own agents and AI apps to get a user token for the Spotify API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Spotify auth provider in your own agents and AI apps to get a user token for the Spotify API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the Spotify API: @@ -151,9 +151,9 @@ const token = authResponse.context.token; ## Using Spotify auth in custom tools -You can use the pre-built [Arcade Spotify MCP Server](/resources/integrations/entertainment/spotify) to quickly build agents and AI apps that interact with Spotify. +You can use the pre-built [Arcade Spotify MCP Server](/en/resources/integrations/entertainment/spotify) to quickly build agents and AI apps that interact with Spotify. -If the pre-built tools in the Spotify MCP Server don't meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Spotify API. +If the pre-built tools in the Spotify MCP Server don't meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Spotify API. Use the `Spotify()` auth class to specify that a tool requires authorization with Spotify. The `context.authorization.token` field will be automatically populated with the user's Spotify token: diff --git a/app/en/references/auth-providers/square/page.mdx b/content/docs/references/auth-providers/square.mdx similarity index 89% rename from app/en/references/auth-providers/square/page.mdx rename to content/docs/references/auth-providers/square.mdx index 1fa50becb..5dcd9c17b 100644 --- a/app/en/references/auth-providers/square/page.mdx +++ b/content/docs/references/auth-providers/square.mdx @@ -1,12 +1,12 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Square +--- +title: Square +--- The Square auth provider enables tools and agents to call [Square APIs](https://developer.squareup.com/reference/square) on behalf of a user using OAuth 2.0 authentication. <Callout> Want to quickly get started with Square in your agent or AI app? The pre-built - [Arcade Square MCP Server](/resources/integrations/productivity/squareup-api) + [Arcade Square MCP Server](/en/resources/integrations/productivity/squareup-api) is what you want! </Callout> @@ -16,7 +16,7 @@ This page describes how to use and configure Square auth with Arcade. This auth provider is used by: -- The [Arcade Square MCP Server](/resources/integrations/productivity/squareup-api), which provides pre-built tools for interacting with Square +- The [Arcade Square MCP Server](/en/resources/integrations/productivity/squareup-api), which provides pre-built tools for interacting with Square - Your [app code](#using-square-auth-in-app-code) that needs to call the Square API - Or, your [custom tools](#using-square-auth-in-custom-tools) that need to call the Square API @@ -24,14 +24,14 @@ This auth provider is used by: Arcade offers a default Square auth provider that you can use in the Arcade Cloud Platform. In this case, your users will see `Arcade` as the name of the application requesting permission. -If you choose to use Arcade's Square auth, you don't need to configure anything. Follow the [Square MCP Server examples](/resources/integrations/productivity/squareup-api) to get started calling Square tools. +If you choose to use Arcade's Square auth, you don't need to configure anything. Follow the [Square MCP Server examples](/en/resources/integrations/productivity/squareup-api) to get started calling Square tools. ## Configuring Square auth <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -121,7 +121,7 @@ Hit the **Create** button and the provider will be ready to be used. <Callout type="info"> This method is only available when you are [self-hosting the - engine](/operate/deploy/helm) + engine](/en/operate/deploy/helm) </Callout> <Steps> @@ -165,11 +165,11 @@ Setting `provider_id: squareup` applies the Arcade Engine's built-in Square conf </Tabs.Tab> </Tabs> -When you use tools that require Square auth using your Arcade account credentials, Arcade will automatically use this Square OAuth provider. If you have multiple Square providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Square auth using your Arcade account credentials, Arcade will automatically use this Square OAuth provider. If you have multiple Square providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. ## Using Square auth in app code -Use the Square auth provider in your own agents and AI apps to get a user token for the Square API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Square auth provider in your own agents and AI apps to get a user token for the Square API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the Square API: @@ -235,9 +235,9 @@ const token = authResponse.context.token; ## Using Square auth in custom tools -You can use the pre-built [Arcade Square MCP Server](/resources/integrations/productivity/squareup-api) to quickly build agents and AI apps that interact with Square. +You can use the pre-built [Arcade Square MCP Server](/en/resources/integrations/productivity/squareup-api) to quickly build agents and AI apps that interact with Square. -If the pre-built tools in the Square MCP Server don't meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Square API. +If the pre-built tools in the Square MCP Server don't meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Square API. Arcade does not have a dedicated Square auth class. Use the `OAuth2()` auth class to specify that a tool requires authorization with Square. The `id` must match the ID of the Square provider you created. To use Arcade's default Square provider, set `id="arcade-squareup"`. The `context.authorization.token` field will be automatically populated with the user's Square token: diff --git a/app/en/references/auth-providers/ticktick/page.mdx b/content/docs/references/auth-providers/ticktick.mdx similarity index 89% rename from app/en/references/auth-providers/ticktick/page.mdx rename to content/docs/references/auth-providers/ticktick.mdx index 66a692311..b86e50986 100644 --- a/app/en/references/auth-providers/ticktick/page.mdx +++ b/content/docs/references/auth-providers/ticktick.mdx @@ -1,13 +1,13 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# TickTick +--- +title: TickTick +--- The TickTick auth provider enables tools and agents to call [TickTick APIs](https://developer.ticktick.com/docs) on behalf of a user using OAuth 2.0 authentication. <Callout> Want to quickly get started with TickTick in your agent or AI app? The pre-built [Arcade TickTick API MCP - Server](/resources/integrations/productivity/ticktick-api) is what you want! + Server](/en/resources/integrations/productivity/ticktick-api) is what you want! </Callout> ### What's documented here @@ -16,7 +16,7 @@ This page describes how to use and configure TickTick auth with Arcade. This auth provider is used by: -- The [Arcade TickTick API MCP Server](/resources/integrations/productivity/ticktick-api), which provides pre-built tools for interacting with TickTick +- The [Arcade TickTick API MCP Server](/en/resources/integrations/productivity/ticktick-api), which provides pre-built tools for interacting with TickTick - Your [app code](#using-ticktick-auth-in-app-code) that needs to call the TickTick API - Or, your [custom tools](#using-ticktick-auth-in-custom-tools) that need to call the TickTick API @@ -24,14 +24,14 @@ This auth provider is used by: Arcade offers a default TickTick auth provider that you can use in the Arcade Cloud Platform. In this case, your users will see `Arcade` as the name of the application requesting permission. -If you choose to use Arcade's TickTick auth, you don't need to configure anything. Follow the [TickTick API MCP Server examples](/resources/integrations/productivity/ticktick-api) to get started calling TickTick tools. +If you choose to use Arcade's TickTick auth, you don't need to configure anything. Follow the [TickTick API MCP Server examples](/en/resources/integrations/productivity/ticktick-api) to get started calling TickTick tools. ## Configuring TickTick auth <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -114,7 +114,7 @@ Hit the **Create** button and the provider will be ready to be used. <Callout type="info"> This method is only available when you are [self-hosting the - engine](/operate/deploy/helm) + engine](/en/operate/deploy/helm) </Callout> <Steps> @@ -158,11 +158,11 @@ Setting `provider_id: ticktick` applies the Arcade Engine's built-in TickTick co </Tabs.Tab> </Tabs> -When you use tools that require TickTick auth using your Arcade account credentials, Arcade will automatically use this TickTick OAuth provider. If you have multiple TickTick providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require TickTick auth using your Arcade account credentials, Arcade will automatically use this TickTick OAuth provider. If you have multiple TickTick providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. ## Using TickTick auth in app code -Use the TickTick auth provider in your own agents and AI apps to get a user token for the TickTick API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the TickTick auth provider in your own agents and AI apps to get a user token for the TickTick API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the TickTick API: @@ -228,9 +228,9 @@ const token = authResponse.context.token; ## Using TickTick auth in custom tools -You can use the pre-built [Arcade TickTick API MCP Server](/resources/integrations/productivity/ticktick-api) to quickly build agents and AI apps that interact with TickTick. +You can use the pre-built [Arcade TickTick API MCP Server](/en/resources/integrations/productivity/ticktick-api) to quickly build agents and AI apps that interact with TickTick. -If the pre-built tools in the TickTick MCP Server don't meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the TickTick API. +If the pre-built tools in the TickTick MCP Server don't meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the TickTick API. Arcade does not have a dedicated TickTick auth class. Use the `OAuth2()` auth class to specify that a tool requires authorization with TickTick. The `id` must match the ID of the TickTick provider you created. To use Arcade's default TickTick provider, set `id="arcade-ticktick"`. The `context.authorization.token` field will be automatically populated with the user's TickTick token: diff --git a/app/en/references/auth-providers/twitch/page.mdx b/content/docs/references/auth-providers/twitch.mdx similarity index 92% rename from app/en/references/auth-providers/twitch/page.mdx rename to content/docs/references/auth-providers/twitch.mdx index 32bd94c9a..3405f2bad 100644 --- a/app/en/references/auth-providers/twitch/page.mdx +++ b/content/docs/references/auth-providers/twitch.mdx @@ -1,6 +1,6 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Twitch +--- +title: Twitch +--- <Callout type="info"> At this time, Arcade does not offer a default Twitch auth provider. To use @@ -24,7 +24,7 @@ This auth provider is used by: <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -80,14 +80,14 @@ Hit the **Create** button and the provider will be ready to be used. </Steps> -When you use tools that require Twitch auth using your Arcade account credentials, Arcade will automatically use this Twitch OAuth provider. If you have multiple Twitch providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Twitch auth using your Arcade account credentials, Arcade will automatically use this Twitch OAuth provider. If you have multiple Twitch providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. </Tabs.Tab> </Tabs> ## Using Twitch auth in app code -Use the Twitch auth provider in your own agents and AI apps to get a user-scoped token for the Twitch API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Twitch auth provider in your own agents and AI apps to get a user-scoped token for the Twitch API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the Twitch API: @@ -153,7 +153,7 @@ const token = authResponse.context.token; ## Using Twitch auth in custom tools -You can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Twitch API. +You can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Twitch API. Use the `Twitch()` auth class to specify that a tool requires authorization with Twitch. The `context.authorization.token` field will be automatically populated with the user's Twitch token: diff --git a/app/en/references/auth-providers/x/page.mdx b/content/docs/references/auth-providers/x.mdx similarity index 87% rename from app/en/references/auth-providers/x/page.mdx rename to content/docs/references/auth-providers/x.mdx index 0671d2bc7..057e246e2 100644 --- a/app/en/references/auth-providers/x/page.mdx +++ b/content/docs/references/auth-providers/x.mdx @@ -1,12 +1,12 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# X +--- +title: X +--- The X auth provider enables tools and agents to call the X (Twitter) API on behalf of a user. <Callout> Want to quickly get started with X services in your agent or AI app? The - pre-built [Arcade X MCP Server](/resources/integrations/social/x) is what you + pre-built [Arcade X MCP Server](/en/resources/integrations/social/x) is what you want! </Callout> @@ -16,7 +16,7 @@ This page describes how to use and configure X auth with Arcade. This auth provider is used by: -- The [Arcade X MCP Server](/resources/integrations/social/x), which provides pre-built tools for interacting with X +- The [Arcade X MCP Server](/en/resources/integrations/social/x), which provides pre-built tools for interacting with X - Your [app code](#using-x-auth-in-app-code) that needs to call X APIs - Or, your [custom tools](#using-x-auth-in-custom-tools) that need to call X APIs @@ -24,14 +24,14 @@ This auth provider is used by: Arcade offers a default X auth provider that you can use in the Arcade Cloud Platform. In this case, your users will see `Arcade` as the name of the application requesting permission. -If you choose to use Arcade's X auth, you don't need to configure anything. Follow the [X MCP Server examples](/resources/integrations/social/x) to get started calling X tools. +If you choose to use Arcade's X auth, you don't need to configure anything. Follow the [X MCP Server examples](/en/resources/integrations/social/x) to get started calling X tools. ## Configuring X auth <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -85,14 +85,14 @@ Hit the **Create** button and the provider will be ready to be used. </Steps> -When you use tools that require X auth using your Arcade account credentials, Arcade will automatically use this X OAuth provider. If you have multiple X providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require X auth using your Arcade account credentials, Arcade will automatically use this X OAuth provider. If you have multiple X providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. </Tabs.Tab> </Tabs> ## Using X auth in app code -Use the X auth provider in your own agents and AI apps to get a user token for the X API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the X auth provider in your own agents and AI apps to get a user token for the X API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for X: @@ -158,9 +158,9 @@ const token = authResponse.context.token; ## Using X auth in custom tools -You can use the pre-built [Arcade X MCP Server](/resources/integrations/social/x) to quickly build agents and AI apps that interact with X. +You can use the pre-built [Arcade X MCP Server](/en/resources/integrations/social/x) to quickly build agents and AI apps that interact with X. -If the pre-built tools in the X MCP Server don't meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the X API. +If the pre-built tools in the X MCP Server don't meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the X API. Use the `X()` auth class to specify that a tool requires authorization with X. The `context.authorization.token` field will be automatically populated with the user's X token: diff --git a/app/en/references/auth-providers/zendesk/page.mdx b/content/docs/references/auth-providers/zendesk.mdx similarity index 91% rename from app/en/references/auth-providers/zendesk/page.mdx rename to content/docs/references/auth-providers/zendesk.mdx index 489a3f36c..195779f5f 100644 --- a/app/en/references/auth-providers/zendesk/page.mdx +++ b/content/docs/references/auth-providers/zendesk.mdx @@ -1,11 +1,12 @@ -import { Tabs, Callout, Steps } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; +--- +title: Zendesk +--- -# Zendesk +import { SignupLink } from "@/components/analytics"; <Callout type="info"> Arcade does not offer a default Zendesk Auth Provider. To use Zendesk auth, - you must create a [custom provider configuration](/references/auth-providers/oauth2) + you must create a [custom provider configuration](/en/references/auth-providers/oauth2) as described below. </Callout> @@ -17,7 +18,7 @@ This page describes how to use and configure Zendesk auth with Arcade. This auth provider is used by: -- The [Arcade Zendesk MCP Server](/resources/integrations/customer-support/zendesk), which provides pre-built tools for interacting with Zendesk services +- The [Arcade Zendesk MCP Server](/en/resources/integrations/customer-support/zendesk), which provides pre-built tools for interacting with Zendesk services - Your [app code](#using-zendesk-auth-in-app-code) that needs to call Zendesk APIs - Or, your [custom tools](#using-zendesk-auth-in-custom-tools) that need to call Zendesk APIs @@ -26,7 +27,7 @@ This auth provider is used by: <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -119,7 +120,7 @@ Click the **Create** button. ## Using Zendesk auth in app code -Use the Zendesk auth provider you just created in your own agents and AI apps to get a user token for Zendesk APIs. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Zendesk auth provider you just created in your own agents and AI apps to get a user token for Zendesk APIs. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for Zendesk APIs: @@ -185,7 +186,7 @@ const token = authResponse.context.token; ## Using Zendesk auth in custom tools -If the [Arcade Zendesk MCP Server](/resources/integrations/customer-support/zendesk) does not meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with Zendesk APIs. +If the [Arcade Zendesk MCP Server](/en/resources/integrations/customer-support/zendesk) does not meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with Zendesk APIs. Use the `OAuth2()` auth class to specify that a tool requires authorization with Zendesk. `context.get_auth_token_or_empty()` returns the user's Zendesk token: diff --git a/app/en/references/auth-providers/zoho/page.mdx b/content/docs/references/auth-providers/zoho.mdx similarity index 91% rename from app/en/references/auth-providers/zoho/page.mdx rename to content/docs/references/auth-providers/zoho.mdx index f603216dc..f55193127 100644 --- a/app/en/references/auth-providers/zoho/page.mdx +++ b/content/docs/references/auth-providers/zoho.mdx @@ -1,14 +1,14 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Zoho +--- +title: Zoho +--- The Zoho auth provider enables tools and agents to call [Zoho APIs](https://www.zoho.com/developer/) on behalf of a user using OAuth 2.0 authentication. <Callout> Want to quickly get started with Zoho in your agent or AI app? Check out the pre-built Arcade Zoho MCP Servers: the [Zoho Books API MCP - Server](/resources/integrations/payments/zoho-books-api) and the [Zoho Creator - API MCP Server](/resources/integrations/development/zoho-creator-api). + Server](/en/resources/integrations/payments/zoho-books-api) and the [Zoho Creator + API MCP Server](/en/resources/integrations/development/zoho-creator-api). </Callout> ### What's documented here @@ -17,8 +17,8 @@ This page describes how to use and configure Zoho auth with Arcade. This auth provider is used by: -- The [Arcade Zoho Books API MCP Server](/resources/integrations/payments/zoho-books-api), which provides pre-built tools for interacting with Zoho Books -- The [Arcade Zoho Creator API MCP Server](/resources/integrations/development/zoho-creator-api), which provides pre-built tools for interacting with Zoho Creator +- The [Arcade Zoho Books API MCP Server](/en/resources/integrations/payments/zoho-books-api), which provides pre-built tools for interacting with Zoho Books +- The [Arcade Zoho Creator API MCP Server](/en/resources/integrations/development/zoho-creator-api), which provides pre-built tools for interacting with Zoho Creator - Your [app code](#using-zoho-auth-in-app-code) that needs to call Zoho APIs - Or, your [custom tools](#using-zoho-auth-in-custom-tools) that need to call Zoho APIs @@ -26,14 +26,14 @@ This auth provider is used by: Arcade offers a default Zoho auth provider that you can use in the Arcade Cloud Platform. In this case, your users will see `Arcade` as the name of the application requesting permission. The default provider signs users in through the US data center (`accounts.zoho.com`). -If you choose to use Arcade's Zoho auth, you don't need to configure anything. Follow the [Zoho Books API MCP Server examples](/resources/integrations/payments/zoho-books-api) to get started calling Zoho tools. +If you choose to use Arcade's Zoho auth, you don't need to configure anything. Follow the [Zoho Books API MCP Server examples](/en/resources/integrations/payments/zoho-books-api) to get started calling Zoho tools. ## Configuring Zoho auth <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -134,7 +134,7 @@ Hit the **Create** button and the provider will be ready to be used. <Callout type="info"> This method is only available when you are [self-hosting the - engine](/operate/deploy/helm) + engine](/en/operate/deploy/helm) </Callout> <Steps> @@ -224,11 +224,11 @@ auth: </Tabs.Tab> </Tabs> -When you use tools that require Zoho auth using your Arcade account credentials, Arcade will automatically use this Zoho OAuth provider. If you have multiple Zoho providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Zoho auth using your Arcade account credentials, Arcade will automatically use this Zoho OAuth provider. If you have multiple Zoho providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. ## Using Zoho auth in app code -Use the Zoho auth provider in your own agents and AI apps to get a user token for Zoho APIs. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Zoho auth provider in your own agents and AI apps to get a user token for Zoho APIs. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for Zoho APIs: @@ -294,9 +294,9 @@ const token = authResponse.context.token; ## Using Zoho auth in custom tools -You can use the pre-built Arcade Zoho MCP Servers ([Zoho Books](/resources/integrations/payments/zoho-books-api), [Zoho Creator](/resources/integrations/development/zoho-creator-api)) to quickly build agents and AI apps that interact with Zoho. +You can use the pre-built Arcade Zoho MCP Servers ([Zoho Books](/en/resources/integrations/payments/zoho-books-api), [Zoho Creator](/en/resources/integrations/development/zoho-creator-api)) to quickly build agents and AI apps that interact with Zoho. -If the pre-built tools don't meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with Zoho APIs. +If the pre-built tools don't meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with Zoho APIs. Arcade does not have a dedicated Zoho auth class. Use the `OAuth2()` auth class to specify that a tool requires authorization with Zoho. The `id` must match the ID of the Zoho provider you created. To use Arcade's default Zoho provider, set `id="arcade-zoho"`. The `context.authorization.token` field will be automatically populated with the user's Zoho token: diff --git a/app/en/references/auth-providers/zoom/page.mdx b/content/docs/references/auth-providers/zoom.mdx similarity index 89% rename from app/en/references/auth-providers/zoom/page.mdx rename to content/docs/references/auth-providers/zoom.mdx index 296d1d73b..96a98f736 100644 --- a/app/en/references/auth-providers/zoom/page.mdx +++ b/content/docs/references/auth-providers/zoom.mdx @@ -1,6 +1,6 @@ -import { Tabs, Callout, Steps } from "nextra/components"; - -# Zoom +--- +title: Zoom +--- The Zoom auth provider enables tools and agents to call the Zoom API on behalf of a user. @@ -10,7 +10,7 @@ This page describes how to use and configure Zoom auth with Arcade. This auth provider is used by: -- The [Arcade Zoom MCP Server](/resources/integrations/social/zoom), which provides pre-built tools for interacting with Zoom +- The [Arcade Zoom MCP Server](/en/resources/integrations/social/zoom), which provides pre-built tools for interacting with Zoom - Your [app code](#using-zoom-auth-in-app-code) that needs to call Zoom APIs - Or, your [custom tools](#using-zoom-auth-in-custom-tools) that need to call Zoom APIs @@ -20,7 +20,7 @@ You can either use Arcade's default Zoom OAuth provider (fastest way to get star ## Arcade's default Zoom OAuth provider -Arcade provides a default Zoom OAuth provider (the Arcade-managed Zoom app) that you can use to quickly get started. This provider supports the scopes used by the [Arcade Zoom MCP Server](/resources/integrations/social/zoom) and is shared across all Arcade users. +Arcade provides a default Zoom OAuth provider (the Arcade-managed Zoom app) that you can use to quickly get started. This provider supports the scopes used by the [Arcade Zoom MCP Server](/en/resources/integrations/social/zoom) and is shared across all Arcade users. ### Supported scopes @@ -59,7 +59,7 @@ The default provider has some limitations: <Callout type="info"> When using your own app credentials, make sure you configure your project to use a [custom user - verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). + verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or agent in production. </Callout> @@ -113,14 +113,14 @@ Hit the **Create** button and the provider will be ready to be used. </Steps> -When you use tools that require Zoom auth using your Arcade account credentials, Arcade will automatically use this Zoom OAuth provider. If you have multiple Zoom providers, see [using multiple auth providers of the same type](/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Zoom auth using your Arcade account credentials, Arcade will automatically use this Zoom OAuth provider. If you have multiple Zoom providers, see [using multiple auth providers of the same type](/en/references/auth-providers#using-multiple-providers-of-the-same-type) for more information. </Tabs.Tab> </Tabs> ## Using Zoom auth in app code -Use the Zoom auth provider in your own agents and AI apps to get a user token for the Zoom API. See [authorizing agents with Arcade](/get-started/about-arcade) to understand how this works. +Use the Zoom auth provider in your own agents and AI apps to get a user token for the Zoom API. See [authorizing agents with Arcade](/en/get-started/about-arcade) to understand how this works. Use `client.auth.start()` to get a user token for the Zoom API: @@ -186,9 +186,9 @@ const token = authResponse.context.token; ## Using Zoom auth in custom tools -You can use the pre-built [Arcade Zoom MCP Server](/resources/integrations/social/zoom) to quickly build agents and AI apps that interact with Zoom. +You can use the pre-built [Arcade Zoom MCP Server](/en/resources/integrations/social/zoom) to quickly build agents and AI apps that interact with Zoom. -If the pre-built tools in the Zoom MCP Server don't meet your needs, you can author your own [custom tools](/build/create-tools/tool-basics/build-mcp-server) that interact with the Zoom API. +If the pre-built tools in the Zoom MCP Server don't meet your needs, you can author your own [custom tools](/en/build/create-tools/tool-basics/build-mcp-server) that interact with the Zoom API. Use the `Zoom()` auth class to specify that a tool requires authorization with Zoom. The `context.authorization.token` field will be automatically populated with the user's Zoom token: diff --git a/app/en/references/cli-cheat-sheet/page.mdx b/content/docs/references/cli-cheat-sheet.mdx similarity index 98% rename from app/en/references/cli-cheat-sheet/page.mdx rename to content/docs/references/cli-cheat-sheet.mdx index e5d83ea2b..91592f7d7 100644 --- a/app/en/references/cli-cheat-sheet/page.mdx +++ b/content/docs/references/cli-cheat-sheet.mdx @@ -1,5 +1,6 @@ --- title: Arcade CLI cheat sheet +sidebarTitle: CLI Cheat Sheet description: Quick reference for all Arcade CLI commands, perfect for printing. --- @@ -10,10 +11,8 @@ import { CommandList, CommandBlock, InfoBox -} from '../../../_components/cheat-sheet' -import '../../../cheat-sheet-print.css' - -# Arcade CLI cheat sheet +} from '@/components/cheat-sheet' +import '@/app/cheat-sheet-print.css' <div className="no-print mb-4 p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg border border-blue-200 dark:border-blue-800"> <strong>📄 Print-friendly!</strong> Use your browser's print function (Ctrl/Cmd + P) to get a landscape-oriented version perfect for events and quick reference. The layout will automatically adjust for optimal printing. @@ -60,7 +59,7 @@ import '../../../cheat-sheet-print.css' <InfoBox type="tip">Use `uv` for faster installs and better dependency management</InfoBox> <InfoBox type="tip"> - Using Windows and PowerShell? See [Windows environment setup](/get-started/setup/windows-environment) for install options with and without `uv`. + Using Windows and PowerShell? See [Windows environment setup](/en/get-started/setup/windows-environment) for install options with and without `uv`. </InfoBox> </CheatSheetSection> diff --git a/app/en/references/contextual-access-webhook-api/page.mdx b/content/docs/references/contextual-access-webhook-api.mdx similarity index 77% rename from app/en/references/contextual-access-webhook-api/page.mdx rename to content/docs/references/contextual-access-webhook-api.mdx index 44b930b83..6822d2ed6 100644 --- a/app/en/references/contextual-access-webhook-api/page.mdx +++ b/content/docs/references/contextual-access-webhook-api.mdx @@ -1,13 +1,12 @@ --- -title: "API Reference" -description: "Interactive API reference for the Contextual Access webhook contract" +title: API Reference +sidebarTitle: Contextual Access Webhook API +description: Interactive API reference for the Contextual Access webhook contract --- import SwaggerUI from "swagger-ui-react"; import "swagger-ui-react/swagger-ui.css"; -import "./swaggerHacks.css"; - -# API Reference +import "@/components/pages/references/contextual-access-webhook-api/swaggerHacks.css"; This is the interactive documentation for the **Contextual Access webhook contract** — the HTTP interface your server implements to integrate with Arcade. diff --git a/app/en/references/page.mdx b/content/docs/references/index.mdx similarity index 96% rename from app/en/references/page.mdx rename to content/docs/references/index.mdx index 390069d9b..789de34b8 100644 --- a/app/en/references/page.mdx +++ b/content/docs/references/index.mdx @@ -1,13 +1,12 @@ --- title: References +sidebarTitle: Overview description: Complete reference documentation for Arcade's APIs, MCP servers, and available auth providers. --- import { Button } from "@arcadeai/design-system"; import Link from "next/link"; -# References - Complete reference documentation for Arcade's APIs, MCP servers, and available auth providers. <div className="container mx-auto px-4 py-12"> @@ -26,7 +25,7 @@ Complete reference documentation for Arcade's APIs, MCP servers, and available a management into your agent applications. </p> <div className="flex flex-wrap gap-3"> - <Button render={<Link href="/references/api" />}> + <Button render={<Link href="/en/references/api" />}> View API Reference </Button> </div> @@ -43,7 +42,7 @@ Complete reference documentation for Arcade's APIs, MCP servers, and available a exposing them through the standardized MCP protocol. </p> <div className="flex flex-wrap gap-3"> - <Button render={<Link href="/references/mcp/python" />}> + <Button render={<Link href="/en/references/mcp/python" />}> View SDK Reference </Button> </div> @@ -131,7 +130,7 @@ Complete reference documentation for Arcade's APIs, MCP servers, and available a With Gradle: </p> - ```kotlin filename="build.gradle.kts" + ```kotlin title="build.gradle.kts" implementation("dev.arcade:arcade-java:${arcade-java-version}") ``` @@ -139,7 +138,7 @@ Complete reference documentation for Arcade's APIs, MCP servers, and available a With Maven: </p> - ```xml filename="pom.xml" + ```xml title="pom.xml" <dependency> <groupId>dev.arcade</groupId> <artifactId>arcade-java</artifactId> diff --git a/content/docs/references/mcp/meta.json b/content/docs/references/mcp/meta.json new file mode 100644 index 000000000..b6a53de1b --- /dev/null +++ b/content/docs/references/mcp/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Arcade MCP", + "pages": ["python", "telemetry"] +} diff --git a/app/en/references/mcp/python/context/page.mdx b/content/docs/references/mcp/python/context.mdx similarity index 97% rename from app/en/references/mcp/python/context/page.mdx rename to content/docs/references/mcp/python/context.mdx index d2b660f4f..a77fa1fb1 100644 --- a/app/en/references/mcp/python/context/page.mdx +++ b/content/docs/references/mcp/python/context.mdx @@ -1,12 +1,10 @@ --- -title: "Context - Arcade MCP Python reference" -description: "Reference documentation for the Context class used by MCP tools" +title: Context +metaTitle: Context - Arcade MCP Python reference +description: Reference documentation for the Context class used by MCP tools +full: true --- -import { Callout } from "nextra/components"; - -# Context - The `Context` class is the primary interface for MCP tools to interact with the server, client, and runtime environment. Tools receive a populated `Context` instance as a parameter and should not create instances directly. `Context` extends `ToolContext` (from `arcade_core`), combining runtime capabilities with tool-specific data. diff --git a/app/en/references/mcp/python/errors/page.mdx b/content/docs/references/mcp/python/errors.mdx similarity index 96% rename from app/en/references/mcp/python/errors/page.mdx rename to content/docs/references/mcp/python/errors.mdx index cd6430465..884e355ac 100644 --- a/app/en/references/mcp/python/errors/page.mdx +++ b/content/docs/references/mcp/python/errors.mdx @@ -1,10 +1,10 @@ --- -title: "Errors - Arcade MCP Python reference" -description: "Exception types raised by the MCP server and re-exported from arcade-core" +title: Errors +metaTitle: Errors - Arcade MCP Python reference +description: Exception types raised by the MCP server and re-exported from arcade-core +full: true --- -# Errors - Domain-specific exception types for the MCP server. All exceptions are defined in `arcade_mcp_server.exceptions`. ## MCP exception hierarchy diff --git a/app/en/references/mcp/python/page.mdx b/content/docs/references/mcp/python/index.mdx similarity index 94% rename from app/en/references/mcp/python/page.mdx rename to content/docs/references/mcp/python/index.mdx index e09614252..ef1a0d7aa 100644 --- a/app/en/references/mcp/python/page.mdx +++ b/content/docs/references/mcp/python/index.mdx @@ -1,12 +1,10 @@ --- -title: "Arcade MCP Server - Python overview" -description: "Overview of the arcade-mcp-server Python package and the MCPApp class" +title: Arcade MCP Server - Python overview +sidebarTitle: Overview +description: Overview of the arcade-mcp-server Python package and the MCPApp class +full: true --- -import { Callout } from "nextra/components"; - -# Arcade MCP Server - Python overview - `arcade-mcp-server` is Arcade's framework for building MCP servers. It provides a clean, minimal API to build MCP servers programmatically. The main entry point is `MCPApp`, which handles tool collection, server configuration, and transport setup. @@ -133,7 +131,7 @@ def settings_handler(uri: str) -> str: ... ``` -Decorator for registering resources and resource templates. See [Resources](/references/mcp/python/resources) for full documentation. +Decorator for registering resources and resource templates. See [Resources](/en/references/mcp/python/resources) for full documentation. #### `add_resource` @@ -141,7 +139,7 @@ Decorator for registering resources and resource templates. See [Resources](/ref app.add_resource(uri, name=None, handler=my_handler, ...) ``` -Imperative form of `@app.resource`. See [Resources](/references/mcp/python/resources) for full documentation. +Imperative form of `@app.resource`. See [Resources](/en/references/mcp/python/resources) for full documentation. #### `add_text_resource` @@ -149,7 +147,7 @@ Imperative form of `@app.resource`. See [Resources](/references/mcp/python/resou app.add_text_resource(uri, text="...", name=None, ...) ``` -Register a static text resource. See [Resources](/references/mcp/python/resources) for full documentation. +Register a static text resource. See [Resources](/en/references/mcp/python/resources) for full documentation. #### `add_file_resource` @@ -157,7 +155,7 @@ Register a static text resource. See [Resources](/references/mcp/python/resource app.add_file_resource(uri, path="path/to/file", name=None, ...) ``` -Register a file-backed resource. See [Resources](/references/mcp/python/resources) for full documentation. +Register a file-backed resource. See [Resources](/en/references/mcp/python/resources) for full documentation. #### `add_tool` diff --git a/content/docs/references/mcp/python/meta.json b/content/docs/references/mcp/python/meta.json new file mode 100644 index 000000000..307a8e917 --- /dev/null +++ b/content/docs/references/mcp/python/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Python", + "pages": [ + "index", + "context", + "resources", + "server", + "settings", + "middleware", + "errors" + ] +} diff --git a/app/en/references/mcp/python/middleware/page.mdx b/content/docs/references/mcp/python/middleware.mdx similarity index 97% rename from app/en/references/mcp/python/middleware/page.mdx rename to content/docs/references/mcp/python/middleware.mdx index 1b159aa1d..ef7504e06 100644 --- a/app/en/references/mcp/python/middleware/page.mdx +++ b/content/docs/references/mcp/python/middleware.mdx @@ -1,10 +1,10 @@ --- -title: "Middleware - Arcade MCP Python reference" -description: "Base interfaces and built-in middleware for intercepting and modifying MCP requests and responses" +title: Middleware +metaTitle: Middleware - Arcade MCP Python reference +description: Base interfaces and built-in middleware for intercepting and modifying MCP requests and responses +full: true --- -# Middleware - Middleware allows you to intercept and modify requests and responses at various stages of processing. Each handler receives a `MiddlewareContext` and a `call_next` function to invoke the next handler in the chain. ## Base classes diff --git a/app/en/references/mcp/python/resources/page.mdx b/content/docs/references/mcp/python/resources.mdx similarity index 97% rename from app/en/references/mcp/python/resources/page.mdx rename to content/docs/references/mcp/python/resources.mdx index 28db273b1..775fa6d25 100644 --- a/app/en/references/mcp/python/resources/page.mdx +++ b/content/docs/references/mcp/python/resources.mdx @@ -1,12 +1,10 @@ --- -title: "Resources - Arcade MCP Python reference" -description: "Reference documentation for registering and serving MCP resources with MCPApp" +title: Resources +metaTitle: Resources - Arcade MCP Python reference +description: Reference documentation for registering and serving MCP resources with MCPApp +full: true --- -import { Callout } from "nextra/components"; - -# Resources - MCP resources expose data to clients through URI-addressable endpoints. `MCPApp` provides build-time methods to register static resources, file-backed resources, and dynamic resource handlers -- including URI templates with parameters. diff --git a/app/en/references/mcp/python/server/page.mdx b/content/docs/references/mcp/python/server.mdx similarity index 94% rename from app/en/references/mcp/python/server/page.mdx rename to content/docs/references/mcp/python/server.mdx index 6f86eaf9d..91b9c44bd 100644 --- a/app/en/references/mcp/python/server/page.mdx +++ b/content/docs/references/mcp/python/server.mdx @@ -1,14 +1,12 @@ --- -title: "Server - Arcade MCP Python reference" -description: "Reference documentation for the low-level MCPServer class" +title: Server +metaTitle: Server - Arcade MCP Python reference +description: Reference documentation for the low-level MCPServer class +full: true --- -import { Callout } from "nextra/components"; - -# Server - <Callout type="info"> - Most users should use [`MCPApp`](/references/mcp/python) instead of `MCPServer` directly. `MCPServer` is a low-level API for advanced use cases. + Most users should use [`MCPApp`](/en/references/mcp/python) instead of `MCPServer` directly. `MCPServer` is a low-level API for advanced use cases. </Callout> The `MCPServer` class is the core server implementation that handles MCP protocol messages, middleware orchestration, and component management for tools, resources, and prompts. diff --git a/app/en/references/mcp/python/settings/page.mdx b/content/docs/references/mcp/python/settings.mdx similarity index 98% rename from app/en/references/mcp/python/settings/page.mdx rename to content/docs/references/mcp/python/settings.mdx index 882c25bc1..bb17a716d 100644 --- a/app/en/references/mcp/python/settings/page.mdx +++ b/content/docs/references/mcp/python/settings.mdx @@ -1,10 +1,10 @@ --- -title: "Settings - Arcade MCP Python reference" -description: "Configuration and environment-driven settings for the Arcade MCP Server" +title: Settings +metaTitle: Settings - Arcade MCP Python reference +description: Configuration and environment-driven settings for the Arcade MCP Server +full: true --- -# Settings - `MCPSettings` is the main configuration container for the Arcade MCP Server. All settings classes use [Pydantic Settings](https://docs.pydantic.dev/latest/concepts/pydantic_settings/) with environment variable support. ## `MCPSettings` diff --git a/app/en/references/mcp/telemetry/page.mdx b/content/docs/references/mcp/telemetry.mdx similarity index 94% rename from app/en/references/mcp/telemetry/page.mdx rename to content/docs/references/mcp/telemetry.mdx index 1c0e9db49..27183f2e6 100644 --- a/app/en/references/mcp/telemetry/page.mdx +++ b/content/docs/references/mcp/telemetry.mdx @@ -1,12 +1,9 @@ --- -title: "Arcade MCP Telemetry" -description: "Learn about what data we track when using arcade-mcp" +title: Telemetry +metaTitle: Arcade MCP Telemetry +description: Learn about what data we track when using arcade-mcp --- -import { Tabs } from "nextra/components"; - -# Telemetry - Arcade collects telemetry data about the usage of `arcade-mcp`, our open source MCP development framework. Participation in this program is optional, and you may opt-out if you'd not like to share any information. Note: This page covers telemetry for local and self-hosted use of `arcade-mcp` only. Our cloud products collect different usage data for improvement and troubleshooting as part of the service, as defined in our [terms-of-service](https://www.arcade.dev/terms-of-service). Commands that connect you to your Arcade account (`arcade deploy`, `arcade login`) also require accepting our [terms-of-service](https://www.arcade.dev/terms-of-service), which supersedes this document: @@ -26,7 +23,7 @@ This data will let us better tailor Arcade to the masses, ensuring its continued We track general usage information for the `arcade-mcp` product, including: -- Command invoked (e.g., `arcade new`, `arcade evals`, other commands listed [here](/references/arcade-cli) ) +- Command invoked (e.g., `arcade new`, `arcade evals`, other commands listed [here](/en/references/arcade-cli) ) - Count of local tool executions (we do NOT collect tool names, parameters, or any other personally identifiable information related to the tools you or your agents execute locally or when self-hosted) - General machine information (e.g., macOS/Windows/Linux, whether the command was run within the CLI) - Duration of commands diff --git a/content/docs/references/meta.json b/content/docs/references/meta.json new file mode 100644 index 000000000..ba780d651 --- /dev/null +++ b/content/docs/references/meta.json @@ -0,0 +1,14 @@ +{ + "title": "References", + "icon": "GraduationCap", + "pages": [ + "index", + "[Changelog](https://releases.arcade.dev/preview/)", + "api", + "mcp", + "arcade-cli", + "cli-cheat-sheet", + "contextual-access-webhook-api", + "auth-providers" + ] +} diff --git a/content/docs/resources/contact-us.mdx b/content/docs/resources/contact-us.mdx new file mode 100644 index 000000000..284f13bc8 --- /dev/null +++ b/content/docs/resources/contact-us.mdx @@ -0,0 +1,10 @@ +--- +title: Contact Us +sidebarTitle: Contact +description: Get support and connect with the Arcade team through various channels +--- + +import { ContactCards } from "@/components/pages/resources/contact-us/contact-cards"; + +**We're here to help you (and your agents) succeed!** +<ContactCards /> diff --git a/content/docs/resources/early-access/meta.json b/content/docs/resources/early-access/meta.json new file mode 100644 index 000000000..7f1bc0ffd --- /dev/null +++ b/content/docs/resources/early-access/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Early Access", + "pages": ["[Arcade Registry](/en/resources/registry-early-access)"] +} diff --git a/app/en/resources/examples/page.mdx b/content/docs/resources/examples.mdx similarity index 91% rename from app/en/resources/examples/page.mdx rename to content/docs/resources/examples.mdx index 1aefa332e..5ba134c63 100644 --- a/app/en/resources/examples/page.mdx +++ b/content/docs/resources/examples.mdx @@ -1,6 +1,6 @@ --- -title: "Examples" -description: "Example apps and MCP servers using Arcade's tools and MCP servers in workflows and with agents." +title: Examples +description: Example apps and MCP servers using Arcade's tools and MCP servers in workflows and with agents. --- ## Examples @@ -9,8 +9,7 @@ Get up and running faster with ready-to-use example repositories. Browse sample apps and MCP servers to see how Arcade works in practice, then fork them as a starting point or use them as a reference while building your own projects and integrations. -import { SampleAppCard } from "../../../_components/sample-app-card"; -import { Tabs } from "nextra/components"; +import { SampleAppCard } from "@/components/sample-app-card"; <Tabs items={["Sample apps", "Example MCP servers"]}> <Tabs.Tab> @@ -121,14 +120,14 @@ Built something with Arcade? The team would love to feature it! Submit your app #### How to submit 1. Fork the [Arcade docs repository](https://github.com/ArcadeAI/docs) -2. Add your app to `/app/en/resources/examples/page.mdx` following the existing pattern +2. Add your app to `/content/docs/resources/examples.mdx` following the existing pattern 3. Include a descriptive title, clear description, and appropriate tags 4. Create a pull request with details about your app and its Arcade integration 5. The team will review and potentially feature your app #### Need help? -If you have questions about submitting your app, feel free to [contact the team](/resources/contact-us) or open an issue in the docs repository. +If you have questions about submitting your app, feel free to [contact the team](/en/resources/contact-us) or open an issue in the docs repository. </Tabs.Tab> <Tabs.Tab> @@ -159,14 +158,14 @@ Built an MCP server with Arcade? The team would love to feature it! Submit your #### How to submit 1. Fork the [Arcade docs repository](https://github.com/ArcadeAI/docs) -2. Add your server to `/app/en/resources/examples/page.mdx` following the existing pattern +2. Add your server to `/content/docs/resources/examples.mdx` following the existing pattern 3. Include a descriptive title, clear description, and appropriate tags 4. Create a pull request with details about your server, its Arcade integration, and a link to the public repository. 5. The team will review and potentially feature your server #### Need help? -If you have questions about submitting your MCP server, feel free to [contact the team](/resources/contact-us) or open an issue in the docs repository. +If you have questions about submitting your MCP server, feel free to [contact the team](/en/resources/contact-us) or open an issue in the docs repository. </Tabs.Tab> </Tabs> diff --git a/app/en/resources/faq/page.mdx b/content/docs/resources/faq.mdx similarity index 88% rename from app/en/resources/faq/page.mdx rename to content/docs/resources/faq.mdx index b51096a23..37c6a9f9d 100644 --- a/app/en/resources/faq/page.mdx +++ b/content/docs/resources/faq.mdx @@ -1,21 +1,19 @@ --- -title: "Frequently Asked Questions" -description: "Frequently asked questions about Arcade" +title: Frequently Asked Questions +sidebarTitle: FAQ +description: Frequently asked questions about Arcade --- import { Button } from "@arcadeai/design-system"; import { MessagesSquare } from "lucide-react"; import Link from "next/link"; -import { Tabs } from "nextra/components"; -import { ContactEmail } from "@/app/_components/contact-email"; - -# Frequently Asked Questions +import { ContactEmail } from "@/components/contact-email"; ## What if I need a Tool that Arcade doesn't have? -Arcade makes it easy to build your own tools! You can fork our existing tools, or build your own from scratch. Learn more about [building your own MCP Server](/build/create-tools/tool-basics/build-mcp-server). +Arcade makes it easy to build your own tools! You can fork our existing tools, or build your own from scratch. Learn more about [building your own MCP Server](/en/build/create-tools/tool-basics/build-mcp-server). -If the tools already exist on an MCP server somewhere — one your team runs, or one a vendor hosts — you don't need to rebuild them. [Register it as a remote MCP server](/operate/governance/remote-mcp-servers) and Arcade governs its tools alongside its own. +If the tools already exist on an MCP server somewhere — one your team runs, or one a vendor hosts — you don't need to rebuild them. [Register it as a remote MCP server](/en/operate/governance/remote-mcp-servers) and Arcade governs its tools alongside its own. ## How do I contribute back a Tool to the Registry? @@ -23,7 +21,7 @@ We're always looking for new tools and MCP Servers! If you have a tool that you ## What is the difference between the Arcade CLI and the Arcade Clients? -The [Arcade CLI](/references/arcade-cli) is a command line tool that makes it easy to build, test, and deploy your own tools to production. The [Arcade Client libraries](/references) are what you will use within your own agents and applications to call the tools. +The [Arcade CLI](/en/references/arcade-cli) is a command line tool that makes it easy to build, test, and deploy your own tools to production. The [Arcade Client libraries](/en/references) are what you will use within your own agents and applications to call the tools. ## What is the difference between personal API keys and project API keys? @@ -35,7 +33,7 @@ Arcade now uses **project API keys** as the standard way to authenticate with th - **Clearer organization**: Keys are organized by project, making it easier to manage multiple projects and their respective resources and permissions. - **Simplified key management**: Manage one key per project instead of individual keys for each team member. -If you're still using a personal API key, we recommend migrating to a project API key. Learn more about [API keys](/get-started/setup/api-keys) and how to create them. +If you're still using a personal API key, we recommend migrating to a project API key. Learn more about [API keys](/en/get-started/setup/api-keys) and how to create them. ## Auth FAQ diff --git a/app/en/resources/glossary/page.mdx b/content/docs/resources/glossary.mdx similarity index 86% rename from app/en/resources/glossary/page.mdx rename to content/docs/resources/glossary.mdx index b5893823a..32913c6d8 100644 --- a/app/en/resources/glossary/page.mdx +++ b/content/docs/resources/glossary.mdx @@ -1,10 +1,10 @@ --- -title: "Glossary" -description: "Glossary of terms used in the Arcade platform" +title: Arcade Glossary +metaTitle: Glossary +sidebarTitle: Glossary +description: Glossary of terms used in the Arcade platform --- -# Arcade Glossary - ## Agents and Tools ```mermaid @@ -29,7 +29,7 @@ This (Reason + Act) is the most common agentic pattern, where an LLM runs in a l A 'Context' is an object that is passed to a tool as a parameter when the tool is executed. It contains information about the tool call, the user for which the tool is being called, and any secrets the tool requires to run. -_Learn more about [context](/build/create-tools/tool-basics/runtime-data-access)._ +_Learn more about [context](/en/build/create-tools/tool-basics/runtime-data-access)._ ### Entrypoint File @@ -37,7 +37,7 @@ An 'Entrypoint file' is necessary when building your own MCP servers with Arcade The following is a minimal example of an entrypoint file: -```python filename="server.py" showLineNumbers +```python title="server.py" lineNumbers from arcade_mcp_server import MCPApp app = MCPApp() @@ -65,7 +65,7 @@ An unprotected (anonymous) MCP server can be used by any MCP client on the netwo #### Remote MCP Servers -A remote MCP server is one that Arcade does not host: a server your team runs, a vendor hosts, or an Arcade partner provides. You [register it](/operate/governance/remote-mcp-servers) with a URL, and Arcade proxies it over the Streamable HTTP transport. In the Servers dashboard it carries the `MCP` type badge and shows its URL, as opposed to an Arcade-hosted server, which carries `HTTP` and shows `Arcade Hosted`. Once registered, a remote server gets the same auth handling, tool filtering, health status, logs, and audit trail as any Arcade-hosted server. +A remote MCP server is one that Arcade does not host: a server your team runs, a vendor hosts, or an Arcade partner provides. You [register it](/en/operate/governance/remote-mcp-servers) with a URL, and Arcade proxies it over the Streamable HTTP transport. In the Servers dashboard it carries the `MCP` type badge and shows its URL, as opposed to an Arcade-hosted server, which carries `HTTP` and shows `Arcade Hosted`. Once registered, a remote server gets the same auth handling, tool filtering, health status, logs, and audit trail as any Arcade-hosted server. #### Project and Global Servers @@ -75,17 +75,17 @@ Every MCP server in Arcade has a visibility scope, chosen when you register the A 'tool' is a function that can be called by an agent which performs some action - commonly via an API, filesystem, database, etc. Tools are written in Python and deployed by running a worker which contains the MCP Server's code. Tools are defined by the `@tool()` decorator and will be passed `ToolContext` as the first argument. If a tool has dependencies that are not met (a secret is not provided, for example), the tool will fail to execute. -Tools are commonly referred to by a qualified name that includes their MCP Server. For example, [Gmail.SendEmail](/resources/integrations/productivity/gmail#gmailsendemail) +Tools are commonly referred to by a qualified name that includes their MCP Server. For example, [Gmail.SendEmail](/en/resources/integrations/productivity/gmail#gmailsendemail) -_Learn more about [tools](/build/create-tools/tool-basics/build-mcp-server)._ +_Learn more about [tools](/en/build/create-tools/tool-basics/build-mcp-server)._ #### Optimized tools -[Optimized tools](/build/create-tools/improve/types-of-tools#optimized-tools) are designed from scratch to provide the best performance for LLMs in terms of speed, reliability, accuracy, and cost-effectiveness. +[Optimized tools](/en/build/create-tools/improve/types-of-tools#optimized-tools) are designed from scratch to provide the best performance for LLMs in terms of speed, reliability, accuracy, and cost-effectiveness. #### Unoptimized tools -[Unoptimized tools](/build/create-tools/improve/types-of-tools#unoptimized-tools) are designed to mirror the original HTTP API design of the upstream service. They are not optimized for LLM usage and are not subject to evaluation suites. We recommend thoroughly evaluating each Unoptimized tool with your Agents or chatbots before using it in production. +[Unoptimized tools](/en/build/create-tools/improve/types-of-tools#unoptimized-tools) are designed to mirror the original HTTP API design of the upstream service. They are not optimized for LLM usage and are not subject to evaluation suites. We recommend thoroughly evaluating each Unoptimized tool with your Agents or chatbots before using it in production. Understand why [LLMs perform poorly when](/build/create-tools/improve/types-of-tools#why-llms-perform-poorly when-calling-http-apis) calling HTTP APIs. @@ -93,13 +93,13 @@ Understand why [LLMs perform poorly when](/build/create-tools/improve/types-of-t 'Tool context' is an object that is passed to a tool as a parameter when the tool is executed. It contains information about the tool call, the user for which the tool is being called, and any secrets the tool requires to run. -_Learn more about [tool context](/build/create-tools/tool-basics/runtime-data-access)._ +_Learn more about [tool context](/en/build/create-tools/tool-basics/runtime-data-access)._ ### Tool Recommendation Tool Recommendation lets an agent find the tools relevant to a task without loading every available tool definition into the model's context. -_Learn more about [Tool Recommendation](/operate/governance/mcp-gateways/tool-recommendation)._ +_Learn more about [Tool Recommendation](/en/operate/governance/mcp-gateways/tool-recommendation)._ ### Transport Modes @@ -115,7 +115,7 @@ _Learn more about [transport modes in the MCP Protocol documentation](https://mo MCP is an open standard protocol that enables LLMs to access and use tools and data sources. Arcade tools go further than MCP with enterprise-grade authentication, secure token management, and fine-grained permissions. This allows your tools to be accessible to any LLM that supports MCP, while maintaining security and user privacy. -Arcade Tools can be presented as an MCP server (via the Arcade Engine), allowing any LLM that supports MCP to access your tools. You can also [add remote MCP servers](/operate/governance/remote-mcp-servers) that Arcade does not host to your project, making their tools available to your agents alongside Arcade's own. +Arcade Tools can be presented as an MCP server (via the Arcade Engine), allowing any LLM that supports MCP to access your tools. You can also [add remote MCP servers](/en/operate/governance/remote-mcp-servers) that Arcade does not host to your project, making their tools available to your agents alongside Arcade's own. ## The Arcade Platform @@ -144,7 +144,7 @@ A project is also a trust boundary. Everyone with membership in a project, or wi ### Arcade API -The 'Arcade API' is a set of endpoints that allow you to interact with the Arcade platform. You can use the API to call tools, get tool definitions, manage user auth, and more. See the [Arcade API Reference](/references/api) for more details. +The 'Arcade API' is a set of endpoints that allow you to interact with the Arcade platform. You can use the API to call tools, get tool definitions, manage user auth, and more. See the [Arcade API Reference](/en/references/api) for more details. ### API Key @@ -174,25 +174,25 @@ User Challenges are the count of authorizations performed for any user (specifie An 'auth provider' is a service that your users sign in with to let the agent access their data or take actions on their behalf. This can be a hosted service like Google or Slack, or a custom OAuth provider. Multiple MCP Servers may share the same auth provider (for example, Gmail and Google Drive both use Google's OAuth provider). Custom auth providers are defined in the Arcade Dashboard, or in your Engine YAML if you're self-hosting. -_Learn more about [auth providers](/references/auth-providers)._ +_Learn more about [auth providers](/en/references/auth-providers)._ ### User Source A 'User Source' is an OIDC identity provider connection that authenticates the end users of an MCP Gateway. A project can have multiple User Sources, and one User Source can back many gateways. User Sources are intentionally separate from any identity provider used for Arcade Account sign-in, so administrators and end users can be governed independently. -_Learn more about [User Sources](/operate/identity/user-sources)._ +_Learn more about [User Sources](/en/operate/identity/user-sources)._ ### Authorization Scope An 'authorization scope' is a permission that a user can grant to an agent. This is used to control what the agent can do with the user's data. Available authorization scopes are defined by the authentication provider, and each tool defines the scopes it requires. -Learn more about [authorized tool calling](/build/tool-calling/custom-apps/auth-tool-calling). +Learn more about [authorized tool calling](/en/build/tool-calling/custom-apps/auth-tool-calling). ### Tool Executions A 'tool execution' is a single call to a tool to interact with a remote system or service. The tool execution itself may fail (e.g. the user does not have permission to call the tool), but as long as the execution was able to be routed to a worker, it will be counted. -_Learn more about [tool executions](/build/tool-calling)._ +_Learn more about [tool executions](/en/build/tool-calling)._ ### Standard and Pro Tool Executions @@ -230,7 +230,7 @@ graph TD The 'Arcade client' is the SDK that you use to interact with the Arcade platform. It is how your agent lists and calls tools, tied back to your project via an API key. We offer clients for many popular languages and frameworks, including Python, JavaScript, and more. -_Learn more about [the Arcade clients](/references)._ +_Learn more about [the Arcade clients](/en/references)._ ### Arcade Engine @@ -250,4 +250,4 @@ Arcade MCP is the secure framework for building MCP servers. It is responsible f Evaluations are a way to test the LLMs' ability to select and call your tools. Arcade's eval suite can and should be run as part of the development process to ensure your tools are working as expected. -_Learn more about evaluations [here](/build/create-tools/evaluate-tools/why-evaluate)._ +_Learn more about evaluations [here](/en/build/create-tools/evaluate-tools/why-evaluate)._ diff --git a/content/docs/resources/integrations/customer-support/meta.json b/content/docs/resources/integrations/customer-support/meta.json new file mode 100644 index 000000000..5ca272627 --- /dev/null +++ b/content/docs/resources/integrations/customer-support/meta.json @@ -0,0 +1,20 @@ +{ + "pages": [ + "---Optimized---", + "[Customer.io](/en/resources/integrations/customer-support/customerio)", + "[Freshdesk](/en/resources/integrations/customer-support/freshdesk)", + "[Freshservice](/en/resources/integrations/customer-support/freshservice)", + "[Pylon](/en/resources/integrations/customer-support/pylon)", + "[ServiceNow](/en/resources/integrations/customer-support/servicenow)", + "[Zendesk](/en/resources/integrations/customer-support/zendesk)", + "---Starter---", + "[Customer.io API](/en/resources/integrations/customer-support/customerio-api)", + "[Customer.io Pipelines API](/en/resources/integrations/customer-support/customerio-pipelines-api)", + "[Customer.io Track API](/en/resources/integrations/customer-support/customerio-track-api)", + "[Freshservice API](/en/resources/integrations/customer-support/freshservice-api)", + "[Intercom API](/en/resources/integrations/customer-support/intercom-api)", + "[PagerDuty API](/en/resources/integrations/customer-support/pagerduty-api)", + "[Pylon API](/en/resources/integrations/customer-support/pylon-api)" + ], + "title": "Customer Support" +} diff --git a/content/docs/resources/integrations/databases/meta.json b/content/docs/resources/integrations/databases/meta.json new file mode 100644 index 000000000..1eac3bb13 --- /dev/null +++ b/content/docs/resources/integrations/databases/meta.json @@ -0,0 +1,13 @@ +{ + "pages": [ + "---Optimized---", + "[Clickhouse](/en/resources/integrations/databases/clickhouse)", + "[MongoDB](/en/resources/integrations/databases/mongodb)", + "[Postgres](/en/resources/integrations/databases/postgres)", + "[Snowflake](/en/resources/integrations/databases/snowflake)", + "[YugabyteDB](/en/resources/integrations/databases/yugabytedb)", + "---Starter---", + "[Weaviate API](/en/resources/integrations/databases/weaviate-api)" + ], + "title": "Databases" +} diff --git a/content/docs/resources/integrations/development/meta.json b/content/docs/resources/integrations/development/meta.json new file mode 100644 index 000000000..72efb9772 --- /dev/null +++ b/content/docs/resources/integrations/development/meta.json @@ -0,0 +1,27 @@ +{ + "pages": [ + "---Optimized---", + "[Bright Data](/en/resources/integrations/development/brightdata)", + "[Cursor Agents](/en/resources/integrations/development/cursor-agents)", + "[Datadog](/en/resources/integrations/development/datadog)", + "[Daytona](/en/resources/integrations/development/daytona)", + "[E2B](/en/resources/integrations/development/e2b)", + "[Firecrawl](/en/resources/integrations/development/firecrawl)", + "[Fly.io](/en/resources/integrations/development/fly-io)", + "[GitHub](/en/resources/integrations/development/github)", + "[Math](/en/resources/integrations/development/math)", + "[Mixpanel](/en/resources/integrations/development/mixpanel)", + "[PagerDuty](/en/resources/integrations/development/pagerduty)", + "[PostHog](/en/resources/integrations/development/posthog)", + "[Postman](/en/resources/integrations/development/postman)", + "[Vercel](/en/resources/integrations/development/vercel)", + "---Starter---", + "[Cursor Agents API](/en/resources/integrations/development/cursor-agents-api)", + "[Datadog API](/en/resources/integrations/development/datadog-api)", + "[GitHub API](/en/resources/integrations/development/github-api)", + "[PostHog API](/en/resources/integrations/development/posthog-api)", + "[Vercel API](/en/resources/integrations/development/vercel-api)", + "[Zoho Creator API](/en/resources/integrations/development/zoho-creator-api)" + ], + "title": "Developer Tools" +} diff --git a/content/docs/resources/integrations/entertainment/meta.json b/content/docs/resources/integrations/entertainment/meta.json new file mode 100644 index 000000000..e932ab1cb --- /dev/null +++ b/content/docs/resources/integrations/entertainment/meta.json @@ -0,0 +1,8 @@ +{ + "pages": [ + "---Optimized---", + "[Imgflip](/en/resources/integrations/entertainment/imgflip)", + "[Spotify](/en/resources/integrations/entertainment/spotify)" + ], + "title": "Entertainment" +} diff --git a/app/en/resources/integrations/page.mdx b/content/docs/resources/integrations/index.mdx similarity index 85% rename from app/en/resources/integrations/page.mdx rename to content/docs/resources/integrations/index.mdx index b4d0889e2..8b71e8ceb 100644 --- a/app/en/resources/integrations/page.mdx +++ b/content/docs/resources/integrations/index.mdx @@ -1,11 +1,11 @@ --- title: MCP Servers +sidebarTitle: Overview description: Browse Arcade's catalog of pre-built MCP servers — integrations for GitHub, Slack, Google Workspace, Salesforce, and dozens of other services, ready to use with any MCP-compatible client via the Arcade Engine. +toc: false --- -import Toolkits from "./components/toolkits"; - -# MCP Servers +import Toolkits from "@/components/pages/integrations/toolkits"; Arcade's catalog of pre-built MCP servers covers services across development, sales, productivity, search, databases, payments, customer support, social, and entertainment. Each MCP server exposes a set of Arcade tools that a model can call through any MCP-compatible client, via the Arcade Engine. diff --git a/content/docs/resources/integrations/meta.json b/content/docs/resources/integrations/meta.json new file mode 100644 index 000000000..da8a10147 --- /dev/null +++ b/content/docs/resources/integrations/meta.json @@ -0,0 +1,17 @@ +{ + "title": "Integrations", + "pages": [ + "index", + "productivity", + "social", + "entertainment", + "development", + "payments", + "search", + "sales", + "databases", + "customer-support", + "---Feedback---", + "tool-feedback" + ] +} diff --git a/content/docs/resources/integrations/payments/meta.json b/content/docs/resources/integrations/payments/meta.json new file mode 100644 index 000000000..a1847874f --- /dev/null +++ b/content/docs/resources/integrations/payments/meta.json @@ -0,0 +1,10 @@ +{ + "pages": [ + "---Optimized---", + "[Stripe](/en/resources/integrations/payments/stripe)", + "---Starter---", + "[Stripe API](/en/resources/integrations/payments/stripe_api)", + "[Zoho Books API](/en/resources/integrations/payments/zoho-books-api)" + ], + "title": "Payments & Finance" +} diff --git a/content/docs/resources/integrations/productivity/meta.json b/content/docs/resources/integrations/productivity/meta.json new file mode 100644 index 000000000..e347683c9 --- /dev/null +++ b/content/docs/resources/integrations/productivity/meta.json @@ -0,0 +1,53 @@ +{ + "pages": [ + "---Optimized---", + "[Airtable](/en/resources/integrations/productivity/airtable)", + "[Asana](/en/resources/integrations/productivity/asana)", + "[Ashby](/en/resources/integrations/productivity/ashby)", + "[Calendly](/en/resources/integrations/productivity/calendly)", + "[ClickUp](/en/resources/integrations/productivity/clickup)", + "[Confluence](/en/resources/integrations/productivity/confluence)", + "[Dropbox](/en/resources/integrations/productivity/dropbox)", + "[Figma](/en/resources/integrations/productivity/figma)", + "[Fireflies](/en/resources/integrations/productivity/fireflies)", + "[Forkable](/en/resources/integrations/productivity/forkable)", + "[Gmail](/en/resources/integrations/productivity/gmail)", + "[Google Calendar](/en/resources/integrations/productivity/google-calendar)", + "[Google Contacts](/en/resources/integrations/productivity/google-contacts)", + "[Google Docs](/en/resources/integrations/productivity/google-docs)", + "[Google Drive](/en/resources/integrations/productivity/google-drive)", + "[Google Sheets](/en/resources/integrations/productivity/google-sheets)", + "[Google Slides](/en/resources/integrations/productivity/google-slides)", + "[Granola](/en/resources/integrations/productivity/granola)", + "[Jira](/en/resources/integrations/productivity/jira)", + "[Linear](/en/resources/integrations/productivity/linear)", + "[Microsoft Excel](/en/resources/integrations/productivity/microsoft-excel)", + "[Microsoft OneDrive](/en/resources/integrations/productivity/microsoft-onedrive)", + "[Microsoft Outlook Calendar](/en/resources/integrations/productivity/microsoft-outlook-calendar)", + "[Microsoft Outlook Mail](/en/resources/integrations/productivity/microsoft-outlook-mail)", + "[Microsoft Power BI](/en/resources/integrations/productivity/microsoft-power-bi)", + "[Microsoft PowerPoint](/en/resources/integrations/productivity/microsoft-powerpoint)", + "[Microsoft SharePoint](/en/resources/integrations/productivity/microsoft-sharepoint)", + "[Microsoft Users](/en/resources/integrations/productivity/microsoft-users)", + "[Microsoft Word](/en/resources/integrations/productivity/microsoft-word)", + "[Notion](/en/resources/integrations/productivity/notion)", + "[Resend](/en/resources/integrations/productivity/resend)", + "[Workday](/en/resources/integrations/productivity/workday)", + "---Starter---", + "[Airtable API](/en/resources/integrations/productivity/airtable-api)", + "[Asana API](/en/resources/integrations/productivity/asana-api)", + "[Ashby API](/en/resources/integrations/productivity/ashby-api)", + "[Box API](/en/resources/integrations/productivity/box-api)", + "[Calendly API](/en/resources/integrations/productivity/calendly-api)", + "[ClickUp API](/en/resources/integrations/productivity/clickup-api)", + "[Figma API](/en/resources/integrations/productivity/figma-api)", + "[Luma API](/en/resources/integrations/productivity/luma-api)", + "[Mailchimp API](/en/resources/integrations/productivity/mailchimp-marketing-api)", + "[Miro API](/en/resources/integrations/productivity/miro-api)", + "[SquareUp API](/en/resources/integrations/productivity/squareup-api)", + "[TickTick API](/en/resources/integrations/productivity/ticktick-api)", + "[Trello API](/en/resources/integrations/productivity/trello-api)", + "[Xero API](/en/resources/integrations/productivity/xero-api)" + ], + "title": "Productivity & Docs" +} diff --git a/content/docs/resources/integrations/sales/meta.json b/content/docs/resources/integrations/sales/meta.json new file mode 100644 index 000000000..03eea63d2 --- /dev/null +++ b/content/docs/resources/integrations/sales/meta.json @@ -0,0 +1,20 @@ +{ + "pages": [ + "---Optimized---", + "[Apollo](/en/resources/integrations/sales/apollo)", + "[Attio](/en/resources/integrations/sales/attio)", + "[HubSpot](/en/resources/integrations/sales/hubspot)", + "[Insightly](/en/resources/integrations/sales/insightly)", + "[Salesforce](/en/resources/integrations/sales/salesforce)", + "---Starter---", + "[HubSpot Automation API](/en/resources/integrations/sales/hubspot-automation-api)", + "[HubSpot CMS API](/en/resources/integrations/sales/hubspot-cms-api)", + "[HubSpot Conversations API](/en/resources/integrations/sales/hubspot-conversations-api)", + "[HubSpot CRM API](/en/resources/integrations/sales/hubspot-crm-api)", + "[HubSpot Events API](/en/resources/integrations/sales/hubspot-events-api)", + "[HubSpot Marketing API](/en/resources/integrations/sales/hubspot-marketing-api)", + "[HubSpot Meetings API](/en/resources/integrations/sales/hubspot-meetings-api)", + "[HubSpot Users API](/en/resources/integrations/sales/hubspot-users-api)" + ], + "title": "Sales" +} diff --git a/content/docs/resources/integrations/search/meta.json b/content/docs/resources/integrations/search/meta.json new file mode 100644 index 000000000..9c11eed26 --- /dev/null +++ b/content/docs/resources/integrations/search/meta.json @@ -0,0 +1,22 @@ +{ + "pages": [ + "---Optimized---", + "[Glean](/en/resources/integrations/search/glean)", + "[Google Finance](/en/resources/integrations/search/google_finance)", + "[Google Flights](/en/resources/integrations/search/google_flights)", + "[Google Hotels](/en/resources/integrations/search/google_hotels)", + "[Google Jobs](/en/resources/integrations/search/google_jobs)", + "[Google Maps](/en/resources/integrations/search/google_maps)", + "[Google News](/en/resources/integrations/search/google_news)", + "[Google Search](/en/resources/integrations/search/google_search)", + "[Google Shopping](/en/resources/integrations/search/google_shopping)", + "[Walmart](/en/resources/integrations/search/walmart)", + "[Youtube](/en/resources/integrations/search/youtube)", + "---Starter---", + "[Exa API](/en/resources/integrations/search/exa-api)", + "---Partners---", + "nimble", + "tavily" + ], + "title": "Search Tools" +} diff --git a/app/en/resources/integrations/search/nimble/page.mdx b/content/docs/resources/integrations/search/nimble.mdx similarity index 81% rename from app/en/resources/integrations/search/nimble/page.mdx rename to content/docs/resources/integrations/search/nimble.mdx index 27926c48b..a03f3f091 100644 --- a/app/en/resources/integrations/search/nimble/page.mdx +++ b/content/docs/resources/integrations/search/nimble.mdx @@ -1,13 +1,9 @@ --- -title: "Nimble" -description: "Search, extract, crawl, and map the web. Nimble (Nimbleway) is available in Arcade as a Partner MCP server." +title: Nimble +description: Search, extract, crawl, and map the web. Nimble (Nimbleway) is available in Arcade as a Partner MCP server. --- -import { Callout, Steps } from "nextra/components"; - -# Nimble - -This integration is a remote MCP server offered by [Nimble](https://www.nimbleway.com), an Arcade Partner. It exposes Nimble's web data platform so agents can search, extract, crawl, and map the public web. [Register it in your Arcade project](/operate/governance/remote-mcp-servers) — or organization-wide — and its tools are available to every gateway in that scope, with central governance, authorization, and access control alongside Arcade's native servers. +This integration is a remote MCP server offered by [Nimble](https://www.nimbleway.com), an Arcade Partner. It exposes Nimble's web data platform so agents can search, extract, crawl, and map the public web. [Register it in your Arcade project](/en/operate/governance/remote-mcp-servers) — or organization-wide — and its tools are available to every gateway in that scope, with central governance, authorization, and access control alongside Arcade's native servers. ## MCP server URL @@ -28,7 +24,7 @@ Nimble expects a bearer token in the `Authorization` header on every request. In | Secret name | `NIMBLE_API_KEY` | | Secret value | Your Nimble API key | -Set these under **Advanced settings → Custom headers** when you add the server. See [Remote MCP servers](/operate/governance/remote-mcp-servers#configure-advanced-settings) for the full reference. +Set these under **Advanced settings → Custom headers** when you add the server. See [Remote MCP servers](/en/operate/governance/remote-mcp-servers#configure-advanced-settings) for the full reference. ## What you can do @@ -68,7 +64,7 @@ Open the [Servers dashboard](https://app.arcade.dev/servers) → **Add Server** Under **Advanced settings → Custom headers**, add the `Authorization` header and store your key as described in [Authentication](#authentication), then save. <Callout type="info"> - See the full walkthrough in [Remote MCP servers](/operate/governance/remote-mcp-servers) for advanced settings like connection retries, header secrets, and custom headers. + See the full walkthrough in [Remote MCP servers](/en/operate/governance/remote-mcp-servers) for advanced settings like connection retries, header secrets, and custom headers. </Callout> ### Verify Nimble tools @@ -95,6 +91,6 @@ Arcade handles authorization, credential handling, and audit logging at runtime. - [Nimble MCP server documentation](https://docs.nimbleway.com/integrations/mcp-server/mcp-server) - [Nimble documentation](https://docs.nimbleway.com) -- [Remote MCP servers](/operate/governance/remote-mcp-servers) -- [Create an MCP Gateway](/operate/governance/mcp-gateways/create-via-dashboard) -- [Connect to MCP clients](/get-started/mcp-clients) +- [Remote MCP servers](/en/operate/governance/remote-mcp-servers) +- [Create an MCP Gateway](/en/operate/governance/mcp-gateways/create-via-dashboard) +- [Connect to MCP clients](/en/get-started/mcp-clients) diff --git a/app/en/resources/integrations/search/tavily/page.mdx b/content/docs/resources/integrations/search/tavily.mdx similarity index 78% rename from app/en/resources/integrations/search/tavily/page.mdx rename to content/docs/resources/integrations/search/tavily.mdx index 29ca63c88..4cc8e73bf 100644 --- a/app/en/resources/integrations/search/tavily/page.mdx +++ b/content/docs/resources/integrations/search/tavily.mdx @@ -1,13 +1,9 @@ --- -title: "Tavily" -description: "Enable agents to search the web in real time and extract structured content. Available directly in Arcade as a Partner MCP Server." +title: Tavily +description: Enable agents to search the web in real time and extract structured content. Available directly in Arcade as a Partner MCP Server. --- -import { Callout, Steps } from "nextra/components"; - -# Tavily - -This integration is a remote MCP server offered by [Tavily](https://tavily.com), an Arcade Partner. [Register it in your Arcade project](/operate/governance/remote-mcp-servers) — or organization-wide — and its tools are available to every gateway in that scope, with central governance, authorization, and access control alongside Arcade's native servers. +This integration is a remote MCP server offered by [Tavily](https://tavily.com), an Arcade Partner. [Register it in your Arcade project](/en/operate/governance/remote-mcp-servers) — or organization-wide — and its tools are available to every gateway in that scope, with central governance, authorization, and access control alongside Arcade's native servers. ## MCP Server URL @@ -45,7 +41,7 @@ Go to [tavily.com](https://tavily.com) → **Overview** → **Generate MCP Link* Open the [Servers dashboard](https://app.arcade.dev/servers) → **Add Server** → **Remote MCP**. Paste the URL from the [MCP Server URL](#mcp-server-url) section above (with your API key in place of `YOUR_API_KEY`) and save. <Callout type="info"> - See the full walkthrough in <a href="/operate/governance/remote-mcp-servers">Remote MCP servers</a> for advanced settings like connection retries, OAuth, and custom headers. + See the full walkthrough in <a href="/en/operate/governance/remote-mcp-servers">Remote MCP servers</a> for advanced settings like connection retries, OAuth, and custom headers. </Callout> ### Verify Tavily tools @@ -75,6 +71,6 @@ See the open-source [Financial Intelligence Agent](https://github.com/arcadeai-l ## Resources - [Tavily documentation](https://docs.tavily.com) -- [Remote MCP servers](/operate/governance/remote-mcp-servers) -- [Create an MCP Gateway](/operate/governance/mcp-gateways/create-via-dashboard) -- [Connect to MCP clients](/get-started/mcp-clients) +- [Remote MCP servers](/en/operate/governance/remote-mcp-servers) +- [Create an MCP Gateway](/en/operate/governance/mcp-gateways/create-via-dashboard) +- [Connect to MCP clients](/en/get-started/mcp-clients) diff --git a/content/docs/resources/integrations/social/meta.json b/content/docs/resources/integrations/social/meta.json new file mode 100644 index 000000000..06bcb45ec --- /dev/null +++ b/content/docs/resources/integrations/social/meta.json @@ -0,0 +1,16 @@ +{ + "pages": [ + "---Optimized---", + "[Discord Bot](/en/resources/integrations/social/discord-bot)", + "[LinkedIn](/en/resources/integrations/social/linkedin)", + "[Microsoft Teams](/en/resources/integrations/social/microsoft-teams)", + "[Reddit](/en/resources/integrations/social/reddit)", + "[Slack](/en/resources/integrations/social/slack)", + "[Telegram](/en/resources/integrations/social/telegram)", + "[X](/en/resources/integrations/social/x)", + "[Zoom](/en/resources/integrations/social/zoom)", + "---Starter---", + "[Slack API](/en/resources/integrations/social/slack-api)" + ], + "title": "Social & Communication" +} diff --git a/app/en/resources/integrations/tool-feedback/page.mdx b/content/docs/resources/integrations/tool-feedback.mdx similarity index 51% rename from app/en/resources/integrations/tool-feedback/page.mdx rename to content/docs/resources/integrations/tool-feedback.mdx index 833b82469..9095b112a 100644 --- a/app/en/resources/integrations/tool-feedback/page.mdx +++ b/content/docs/resources/integrations/tool-feedback.mdx @@ -1,12 +1,11 @@ --- -title: "Tool feedback" -description: "Request missing tools, suggest features for existing tools, or report bugs in Arcade tools" +title: Tool feedback +sidebarTitle: Tool Feedback +description: Request missing tools, suggest features for existing tools, or report bugs in Arcade tools --- -import { ToolFeedbackSurvey } from "@/app/_components/tool-feedback-survey"; -import { TakeSurvey } from "./take-survey"; - -# Tool feedback +import { ToolFeedbackSurvey } from "@/components/tool-feedback-survey"; +import { TakeSurvey } from "@/components/pages/resources/integrations/tool-feedback/take-survey"; Tell us what's missing or broken. Whether you need a tool Arcade doesn't yet support, want a new capability added to an existing tool, or have hit a bug, we want to hear about it. diff --git a/content/docs/resources/meta.json b/content/docs/resources/meta.json new file mode 100644 index 000000000..6eda19f88 --- /dev/null +++ b/content/docs/resources/meta.json @@ -0,0 +1,15 @@ +{ + "title": "Resources", + "icon": "Pencil", + "pages": [ + "integrations", + "examples", + "tools", + "glossary", + "faq", + "security-research-program", + "contact-us", + "early-access", + "!registry-early-access" + ] +} diff --git a/app/en/resources/registry-early-access/page.mdx b/content/docs/resources/registry-early-access.mdx similarity index 82% rename from app/en/resources/registry-early-access/page.mdx rename to content/docs/resources/registry-early-access.mdx index 0ae0a81df..36d1cd581 100644 --- a/app/en/resources/registry-early-access/page.mdx +++ b/content/docs/resources/registry-early-access.mdx @@ -1,13 +1,11 @@ --- -title: "Arcade Registry Early Access" -description: "Learn about the Arcade Registry and how to get early access" +title: The Arcade Registry +metaTitle: Arcade Registry Early Access +description: Learn about the Arcade Registry and how to get early access --- -import { EarlyAccessRegistrySurvey } from "@/app/_components/early-access-registry-survey"; -import { Callout } from "nextra/components"; -import { TakeSurvey } from "./take-survey"; - -# The Arcade Registry +import { EarlyAccessRegistrySurvey } from "@/components/early-access-registry-survey"; +import { TakeSurvey } from "@/components/pages/resources/registry-early-access/take-survey"; The **Arcade Registry** is how agentic tool developers share their work with the world. Arcade.dev is collecting all the integrations that agents will ever need in one place - think HuggingFace or Pypi but for LLM tools. A powerful community of open source and for-profit developers building out robust and evaluated workflows is how agents can be elevated to being _truly_ useful. diff --git a/app/en/resources/security-research-program/page.mdx b/content/docs/resources/security-research-program.mdx similarity index 81% rename from app/en/resources/security-research-program/page.mdx rename to content/docs/resources/security-research-program.mdx index 2b2486d5d..3a46dc70d 100644 --- a/app/en/resources/security-research-program/page.mdx +++ b/content/docs/resources/security-research-program.mdx @@ -1,11 +1,10 @@ --- -title: "Security Research Program" -description: "Report security vulnerabilities and help us build safe and reliable tools" +title: Security Research Program +sidebarTitle: Security research program +description: Report security vulnerabilities and help us build safe and reliable tools --- -import { ContactEmail } from "@/app/_components/contact-email"; - -# Security Research Program +import { ContactEmail } from "@/components/contact-email"; At Arcade, security is fundamental to our mission of building safe and reliable tools. We recognize that the security research community plays a valuable role in identifying potential vulnerabilities. @@ -70,7 +69,7 @@ Reports that only recommend `-all`, DNSSEC, RRSIG, or CAA describe hardening cho ### A project API key is an administrator credential -Arcade Cloud isolates data by organization and by [project](/resources/glossary#project). Organizations and projects are trust boundaries. Project administrators and anyone with an API key share access to that project's users, brokered tokens, and secrets. They can also start authorization for those users. Invite only people you trust, and join projects you trust. +Arcade Cloud isolates data by organization and by [project](/en/resources/glossary#project). Organizations and projects are trust boundaries. Project administrators and anyone with an API key share access to that project's users, brokered tokens, and secrets. They can also start authorization for those users. Invite only people you trust, and join projects you trust. A project API key is a service-level credential It can start authorization and read tokens for that project's users by design to support offline and background workloads. A report that uses a project key to read data from the same project describes expected behavior. @@ -80,7 +79,7 @@ A project API key is a service-level credential It can start authorization and r ### User-facing (browser) OAuth flows require verification -To complete authorization, the [user verifier](/build/user-facing-agents/secure-auth-production) must use an API key from the project that started the flow and submit the same `user_id` supplied at the start. The user ID does not travel through the browser redirect. +To complete authorization, the [user verifier](/en/build/user-facing-agents/secure-auth-production) must use an API key from the project that started the flow and submit the same `user_id` supplied at the start. The user ID does not travel through the browser redirect. ### A custom auth provider replaces the platform provider for your tenant @@ -90,10 +89,10 @@ A broken provider configuration can disrupt that project's tools. This reflects ### Default OAuth apps follow project membership -Arcade's default OAuth apps work with the [Arcade user verifier](/build/user-facing-agents/secure-auth-production#use-the-arcade-user-verifier) only. The verifier asks the end-user to sign in to an Arcade Cloud account that is a member of the project. +Arcade's default OAuth apps work with the [Arcade user verifier](/en/build/user-facing-agents/secure-auth-production#use-the-arcade-user-verifier) only. The verifier asks the end-user to sign in to an Arcade Cloud account that is a member of the project. -For a multi-user production app, add your own OAuth client and a [custom user verifier](/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Default, Arcade.dev-branded apps are **only** meant for development, testing, and personal use. +For a multi-user production app, add your own OAuth client and a [custom user verifier](/en/build/user-facing-agents/secure-auth-production#build-a-custom-user-verifier). Default, Arcade.dev-branded apps are **only** meant for development, testing, and personal use. Read [Confused deputy attacks on OAuth](https://www.arcade.dev/blog/arcade-proactively-addressed-coat-vulnerability-in-agentic-ai/) for background on OAuth-related account takeover and Arcade's controls. diff --git a/app/en/resources/tools/page.mdx b/content/docs/resources/tools.mdx similarity index 86% rename from app/en/resources/tools/page.mdx rename to content/docs/resources/tools.mdx index 8c177e6f7..a50e0c6f5 100644 --- a/app/en/resources/tools/page.mdx +++ b/content/docs/resources/tools.mdx @@ -1,4 +1,6 @@ -# Tools +--- +title: Tools +--- Arcade provides a comprehensive ecosystem for AI tools that enable your agents to take real-world actions. Whether you need pre-built integrations or want to create custom functionality, Arcade has you covered. @@ -6,19 +8,19 @@ Arcade provides a comprehensive ecosystem for AI tools that enable your agents t Discover thousands of ready-to-use tools for popular services like Gmail, Slack, GitHub, and more. Our catalog contains production-ready MCP servers that handle authentication, permissions, and API integrations seamlessly. -[Browse Tool Catalog →](/resources/integrations) +[Browse Tool Catalog →](/en/resources/integrations) ## Create Your Own Tools Build custom tools tailored to your specific needs using our powerful development framework. Create MCP servers, define tool schemas, and deploy them to production with ease. -[Learn to Build Tools →](/build/create-tools/tool-basics/build-mcp-server) +[Learn to Build Tools →](/en/build/create-tools/tool-basics/build-mcp-server) ## Contribute to the Catalog Share your tools with the Arcade community and help expand our ecosystem. Submit your MCP servers to make them available to developers worldwide. -[Add Your Tools to the Catalog →](/resources/registry-early-access) +[Add Your Tools to the Catalog →](/en/resources/registry-early-access) ## Why Choose Arcade Tools? diff --git a/app/en/operate/deploy/helm/_chart/1.11.2/values.md b/content/helm-chart/1.11.2/values.md similarity index 100% rename from app/en/operate/deploy/helm/_chart/1.11.2/values.md rename to content/helm-chart/1.11.2/values.md diff --git a/app/_data/partner-toolkits.ts b/data/partner-toolkits.ts similarity index 100% rename from app/_data/partner-toolkits.ts rename to data/partner-toolkits.ts diff --git a/data/upstream-last-modified.json b/data/upstream-last-modified.json new file mode 100644 index 000000000..c309973af --- /dev/null +++ b/data/upstream-last-modified.json @@ -0,0 +1,172 @@ +{ + "build/arcade-deploy.mdx": 1791320176000, + "build/create-tools/error-handling/index.mdx": 1791320176000, + "build/create-tools/error-handling/retry-tools.mdx": 1791320176000, + "build/create-tools/error-handling/useful-tool-errors.mdx": 1791320176000, + "build/create-tools/evaluate-tools/capture-mode.mdx": 1791320176000, + "build/create-tools/evaluate-tools/comparative-evaluations.mdx": 1791320176000, + "build/create-tools/evaluate-tools/create-evaluation-suite.mdx": 1791320176000, + "build/create-tools/evaluate-tools/index.mdx": 1791320176000, + "build/create-tools/evaluate-tools/run-evaluations.mdx": 1791320176000, + "build/create-tools/evaluate-tools/why-evaluate.mdx": 1791320176000, + "build/create-tools/improve/types-of-tools.mdx": 1791320176000, + "build/create-tools/index.mdx": 1791320176000, + "build/create-tools/migrate-toolkits.mdx": 1791320176000, + "build/create-tools/secure-your-server/index.mdx": 1791320176000, + "build/create-tools/secure-your-server/secure-your-mcp-server.mdx": 1791320176000, + "build/create-tools/tool-basics/add-tool-metadata.mdx": 1791320176000, + "build/create-tools/tool-basics/build-mcp-server.mdx": 1791320176000, + "build/create-tools/tool-basics/call-tools-mcp.mdx": 1791320176000, + "build/create-tools/tool-basics/compare-server-types.mdx": 1791320176000, + "build/create-tools/tool-basics/create-tool-auth.mdx": 1791320176000, + "build/create-tools/tool-basics/create-tool-secrets.mdx": 1791320176000, + "build/create-tools/tool-basics/index.mdx": 1791320176000, + "build/create-tools/tool-basics/organize-mcp-tools.mdx": 1791320176000, + "build/create-tools/tool-basics/runtime-data-access.mdx": 1791320176000, + "build/index.mdx": 1791320176000, + "build/tool-calling/call-third-party-apis.mdx": 1791320176000, + "build/tool-calling/custom-apps/auth-tool-calling.mdx": 1791320176000, + "build/tool-calling/custom-apps/check-auth-status.mdx": 1791320176000, + "build/tool-calling/custom-apps/get-tool-definitions.mdx": 1791320176000, + "build/tool-calling/custom-apps/index.mdx": 1791320176000, + "build/tool-calling/error-handling.mdx": 1791320176000, + "build/tool-calling/index.mdx": 1791320176000, + "build/user-facing-agents/index.mdx": 1791320176000, + "build/user-facing-agents/secure-auth-production.mdx": 1791320176000, + "get-started/about-arcade.mdx": 1791320176000, + "get-started/agent-frameworks/ag2/use-arcade-tools.mdx": 1791320176000, + "get-started/agent-frameworks/copilotkit.mdx": 1791320176000, + "get-started/agent-frameworks/crewai/use-arcade-tools.mdx": 1791320176000, + "get-started/agent-frameworks/google-adk/overview.mdx": 1791320176000, + "get-started/agent-frameworks/google-adk/setup-python.mdx": 1791320176000, + "get-started/agent-frameworks/google-adk/setup-typescript.mdx": 1791320176000, + "get-started/agent-frameworks/index.mdx": 1791320176000, + "get-started/agent-frameworks/langchain/auth-langchain-tools.mdx": 1791320176000, + "get-started/agent-frameworks/langchain/overview.mdx": 1791320176000, + "get-started/agent-frameworks/langchain/use-arcade-with-langchain-py.mdx": 1791320176000, + "get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts.mdx": 1791320176000, + "get-started/agent-frameworks/mastra.mdx": 1791320176000, + "get-started/agent-frameworks/openai-agents/overview.mdx": 1791320176000, + "get-started/agent-frameworks/openai-agents/setup-python.mdx": 1791320176000, + "get-started/agent-frameworks/openai-agents/setup-typescript.mdx": 1791320176000, + "get-started/agent-frameworks/setup-arcade-with-your-llm-python.mdx": 1791320176000, + "get-started/agent-frameworks/springai.mdx": 1791320176000, + "get-started/agent-frameworks/tanstack-ai.mdx": 1791320176000, + "get-started/agent-frameworks/vercelai.mdx": 1791320176000, + "get-started/mcp-clients/claude-code.mdx": 1791320176000, + "get-started/mcp-clients/claude-desktop.mdx": 1791320176000, + "get-started/mcp-clients/copilot-studio.mdx": 1791320176000, + "get-started/mcp-clients/cursor.mdx": 1791320176000, + "get-started/mcp-clients/github-copilot.mdx": 1791320176000, + "get-started/mcp-clients/index.mdx": 1791320176000, + "get-started/mcp-clients/visual-studio-code.mdx": 1791320176000, + "get-started/quickstarts/call-tool-agent.mdx": 1791320176000, + "get-started/quickstarts/call-tool-client.mdx": 1791320176000, + "get-started/quickstarts/mcp-server-quickstart.mdx": 1791320176000, + "get-started/setup/api-keys.mdx": 1791320176000, + "get-started/setup/connect-arcade-docs.mdx": 1791320176000, + "get-started/setup/strong-authentication.mdx": 1791320176000, + "get-started/setup/windows-environment.mdx": 1791320176000, + "home.mdx": 1791320176000, + "learn/server-level-vs-tool-level-auth.mdx": 1791320176000, + "operate/deploy/arcade-cloud.mdx": 1791320176000, + "operate/deploy/architecture.mdx": 1791320176000, + "operate/deploy/aws.mdx": 1791320176000, + "operate/deploy/azure.mdx": 1791320176000, + "operate/deploy/gcp.mdx": 1791320176000, + "operate/deploy/helm.mdx": 1791320176000, + "operate/deploy/index.mdx": 1791320176000, + "operate/deploy/on-prem.mdx": 1791320176000, + "operate/deploy/worker.mdx": 1791320176000, + "operate/governance/audit-logs.mdx": 1791320176000, + "operate/governance/contextual-access/build-your-own.mdx": 1791320176000, + "operate/governance/contextual-access/examples.mdx": 1791320176000, + "operate/governance/contextual-access/group-access.mdx": 1791856038000, + "operate/governance/contextual-access/how-hooks-work.mdx": 1791320176000, + "operate/governance/contextual-access/index.mdx": 1791320176000, + "operate/governance/contextual-access/rate-limiting.mdx": 1791320176000, + "operate/governance/index.mdx": 1791320176000, + "operate/governance/mcp-gateways/create-via-ai.mdx": 1791320176000, + "operate/governance/mcp-gateways/create-via-dashboard.mdx": 1791320176000, + "operate/governance/mcp-gateways/index.mdx": 1791320176000, + "operate/governance/private-registry.mdx": 1791320176000, + "operate/governance/remote-mcp-servers/atlassian.mdx": 1791320176000, + "operate/governance/remote-mcp-servers/aws.mdx": 1791320176000, + "operate/governance/remote-mcp-servers/dynamics-365-customer-service.mdx": 1791320176000, + "operate/governance/remote-mcp-servers/github.mdx": 1791320176000, + "operate/governance/remote-mcp-servers/hubspot.mdx": 1791320176000, + "operate/governance/remote-mcp-servers/index.mdx": 1791320176000, + "operate/governance/remote-mcp-servers/salesforce.mdx": 1791320176000, + "operate/governance/remote-mcp-servers/servicenow.mdx": 1791320176000, + "operate/governance/remote-mcp-servers/snowflake.mdx": 1791320176000, + "operate/governance/remote-mcp-servers/splunk.mdx": 1791320176000, + "operate/governance/tool-executions.mdx": 1791320176000, + "operate/identity/index.mdx": 1791320176000, + "operate/identity/user-sources/auth0.mdx": 1791320176000, + "operate/identity/user-sources/clerk.mdx": 1791320176000, + "operate/identity/user-sources/index.mdx": 1791320176000, + "operate/identity/user-sources/microsoft-entra-id.mdx": 1791320176000, + "operate/identity/user-sources/okta.mdx": 1791320176000, + "operate/identity/user-sources/stytch.mdx": 1791320176000, + "operate/index.mdx": 1791320176000, + "operate/quickstart.mdx": 1791320176000, + "references/api.mdx": 1791320176000, + "references/arcade-cli.mdx": 1791320176000, + "references/auth-providers/airtable.mdx": 1791320176000, + "references/auth-providers/asana.mdx": 1791320176000, + "references/auth-providers/atlassian.mdx": 1791320176000, + "references/auth-providers/attio.mdx": 1791320176000, + "references/auth-providers/calendly.mdx": 1791320176000, + "references/auth-providers/cisco-duo.mdx": 1791320176000, + "references/auth-providers/clickup.mdx": 1791320176000, + "references/auth-providers/discord.mdx": 1791320176000, + "references/auth-providers/dropbox.mdx": 1791320176000, + "references/auth-providers/figma.mdx": 1791320176000, + "references/auth-providers/github.mdx": 1791320176000, + "references/auth-providers/google.mdx": 1791320176000, + "references/auth-providers/hubspot.mdx": 1791320176000, + "references/auth-providers/index.mdx": 1791320176000, + "references/auth-providers/linear.mdx": 1791320176000, + "references/auth-providers/linkedin.mdx": 1791320176000, + "references/auth-providers/mailchimp.mdx": 1791320176000, + "references/auth-providers/microsoft-powerbi.mdx": 1791320176000, + "references/auth-providers/microsoft.mdx": 1791320176000, + "references/auth-providers/miro.mdx": 1791320176000, + "references/auth-providers/notion.mdx": 1791320176000, + "references/auth-providers/oauth2.mdx": 1791320176000, + "references/auth-providers/pagerduty.mdx": 1791320176000, + "references/auth-providers/reddit.mdx": 1791320176000, + "references/auth-providers/salesforce.mdx": 1791320176000, + "references/auth-providers/servicenow.mdx": 1791320176000, + "references/auth-providers/slack.mdx": 1791320176000, + "references/auth-providers/spotify.mdx": 1791320176000, + "references/auth-providers/square.mdx": 1791320176000, + "references/auth-providers/ticktick.mdx": 1791320176000, + "references/auth-providers/twitch.mdx": 1791320176000, + "references/auth-providers/x.mdx": 1791320176000, + "references/auth-providers/zendesk.mdx": 1791320176000, + "references/auth-providers/zoho.mdx": 1791320176000, + "references/auth-providers/zoom.mdx": 1791320176000, + "references/cli-cheat-sheet.mdx": 1791320176000, + "references/contextual-access-webhook-api.mdx": 1791320176000, + "references/index.mdx": 1791320176000, + "references/mcp/python/context.mdx": 1791320176000, + "references/mcp/python/errors.mdx": 1791320176000, + "references/mcp/python/index.mdx": 1791320176000, + "references/mcp/python/middleware.mdx": 1791320176000, + "references/mcp/python/resources.mdx": 1791320176000, + "references/mcp/python/server.mdx": 1791320176000, + "references/mcp/python/settings.mdx": 1791320176000, + "references/mcp/telemetry.mdx": 1791320176000, + "resources/contact-us.mdx": 1791320176000, + "resources/examples.mdx": 1791320176000, + "resources/faq.mdx": 1791320176000, + "resources/glossary.mdx": 1791320176000, + "resources/integrations/index.mdx": 1791320176000, + "resources/integrations/search/nimble.mdx": 1791320176000, + "resources/integrations/search/tavily.mdx": 1791320176000, + "resources/integrations/tool-feedback.mdx": 1791320176000, + "resources/registry-early-access.mdx": 1791320176000, + "resources/security-research-program.mdx": 1791320176000, + "resources/tools.mdx": 1791320176000 +} diff --git a/config.ts b/lib/config.ts similarity index 100% rename from config.ts rename to lib/config.ts diff --git a/_dictionaries/en.ts b/lib/dictionaries/en.ts similarity index 100% rename from _dictionaries/en.ts rename to lib/dictionaries/en.ts diff --git a/_dictionaries/get-dictionary.ts b/lib/dictionaries/get-dictionary.ts similarity index 100% rename from _dictionaries/get-dictionary.ts rename to lib/dictionaries/get-dictionary.ts diff --git a/_dictionaries/i18n-config.ts b/lib/dictionaries/i18n-config.ts similarity index 100% rename from _dictionaries/i18n-config.ts rename to lib/dictionaries/i18n-config.ts diff --git a/app/_lib/integration-catalog.ts b/lib/integration-catalog.ts similarity index 96% rename from app/_lib/integration-catalog.ts rename to lib/integration-catalog.ts index fc518ba7f..f133d2160 100644 --- a/app/_lib/integration-catalog.ts +++ b/lib/integration-catalog.ts @@ -1,6 +1,6 @@ import type { Toolkit } from "@arcadeai/design-system"; import { TOOLKITS } from "@arcadeai/design-system/metadata/toolkits"; -import { PARTNER_TOOLKITS } from "@/app/_data/partner-toolkits"; +import { PARTNER_TOOLKITS } from "@/data/partner-toolkits"; import { normalizeToolkitId } from "@/toolkit-docs-generator/src/shared/toolkit-primitives"; import { readToolkitData } from "./toolkit-data"; import type { ToolkitWithDocsLink } from "./toolkit-slug"; diff --git a/app/_lib/integration-index.ts b/lib/integration-index.ts similarity index 100% rename from app/_lib/integration-index.ts rename to lib/integration-index.ts diff --git a/lib/last-modified.ts b/lib/last-modified.ts new file mode 100644 index 000000000..a57caad2d --- /dev/null +++ b/lib/last-modified.ts @@ -0,0 +1,38 @@ +import upstreamDates from "@/data/upstream-last-modified.json"; + +/** + * Pages imported from the Nextra repo keep the "Last updated on" date they had + * there: this repo's git history starts at the import, so its own dates would + * all read as the import day. `data/upstream-last-modified.json` holds each + * page's date as production displayed it at the moment of the import. + * + * A page edited after the import has a later commit here, and that date wins. + */ +const IMPORTED_AT = Date.parse("2026-10-14T00:00:00Z"); + +const UPSTREAM_DATES: Record<string, number> = upstreamDates; + +export function pageLastModified( + contentPath: string, + gitDate: string | null +): string | null { + const upstream = UPSTREAM_DATES[contentPath]; + if (upstream === undefined) { + return gitDate; + } + if (gitDate && Date.parse(gitDate) > IMPORTED_AT) { + return gitDate; + } + return new Date(upstream).toISOString(); +} + +/** + * Toolkit pages are generated from data, so no content file carries their + * date. Production dates them all by its single dynamic route file; this is + * that date at the moment of the import. + */ +const TOOLKIT_PAGES_UPDATED_AT = Date.parse("2026-10-06T20:56:16Z"); + +export function toolkitPageLastModified(): string { + return new Date(TOOLKIT_PAGES_UPDATED_AT).toISOString(); +} diff --git a/lib/layout.shared.tsx b/lib/layout.shared.tsx new file mode 100644 index 000000000..0163865d0 --- /dev/null +++ b/lib/layout.shared.tsx @@ -0,0 +1,14 @@ +import { urlConfig } from "@/lib/config"; + +/** + * Links shown in the docs chrome (components/chrome/navbar.tsx). The contact + * address never appears here as a `mailto:` link; see + * tests/chrome-no-mailto.test.ts. + */ +export const GITHUB_REPO_URL = "https://github.com/ArcadeAI/arcade-mcp"; +export const DISCORD_URL = urlConfig.company.discord; +/** The navbar logo links to the marketing site, not the docs home. */ +export const LOGO_URL = urlConfig.company.url; +/** Base of the "Edit this page on GitHub" link. */ +export const DOCS_REPOSITORY_BASE = + "https://github.com/ArcadeAI/docs/tree/main"; diff --git a/lib/next-plugin-llmstxt.ts b/lib/next-plugin-llmstxt.ts deleted file mode 100644 index e8ab7929c..000000000 --- a/lib/next-plugin-llmstxt.ts +++ /dev/null @@ -1,72 +0,0 @@ -import type { NextConfig } from "next"; - -type LlmsTxtPluginOptions = { - enabled?: boolean; - generateOnBuild?: boolean; -}; - -/** - * Next.js plugin that generates llms.txt at build time - * - * This plugin integrates with the Next.js build process to automatically - * generate an llms.txt file that follows the llms.txt specification. - * - * @param options - Plugin configuration options - * @returns Next.js config wrapper function - */ -export function withLlmsTxt(options: LlmsTxtPluginOptions = {}) { - const { enabled = true, generateOnBuild = true } = options; - - return (nextConfig: NextConfig = {}): NextConfig => { - if (!enabled) { - return nextConfig; - } - - return { - ...nextConfig, - webpack: (config, context) => { - // Only run during production build or when explicitly enabled - if (generateOnBuild && !context.isServer) { - // Add a custom plugin that runs after compilation - config.plugins = config.plugins || []; - config.plugins.push({ - apply: (compiler: { - hooks: { - afterEmit: { - tapPromise: ( - name: string, - callback: () => Promise<void> - ) => void; - }; - }; - options: { name: string }; - }) => { - compiler.hooks.afterEmit.tapPromise("LlmsTxtPlugin", async () => { - // Only generate once (not on every rebuild) - if (compiler.options.name === "client") { - try { - // Dynamic import to avoid bundling issues - const { generateLlmsTxt } = await import( - "../scripts/generate-llmstxt.js" - ); - - await generateLlmsTxt(); - } catch (_error) { - // Don't fail the build if llms.txt generation fails - } - } - }); - }, - }); - } - - // Call the original webpack function if it exists - if (typeof nextConfig.webpack === "function") { - return nextConfig.webpack(config, context); - } - - return config; - }, - }; - }; -} diff --git a/app/_lib/ory-session-context.tsx b/lib/ory-session-context.tsx similarity index 100% rename from app/_lib/ory-session-context.tsx rename to lib/ory-session-context.tsx diff --git a/app/_lib/posthog-server.ts b/lib/posthog-server.ts similarity index 100% rename from app/_lib/posthog-server.ts rename to lib/posthog-server.ts diff --git a/lib/remark-glossary.ts b/lib/remark-glossary.ts index 6309ed010..2bd44da1f 100644 --- a/lib/remark-glossary.ts +++ b/lib/remark-glossary.ts @@ -1,4 +1,4 @@ -import type { Paragraph, PhrasingContent, Root, Text } from "mdast"; +import type { Paragraph, Parent, PhrasingContent, Root, Text } from "mdast"; import { SKIP, visit } from "unist-util-visit"; import { type GlossaryTerm, @@ -12,63 +12,162 @@ type RemarkGlossaryOptions = { caseSensitive?: boolean; }; +/** The subset of a `VFile` this plugin reads. */ +type FileLike = { + history?: string[]; + path?: string; +}; + +/** Minimal shape of an `mdxJsxTextElement` (from mdast-util-mdx-jsx). */ +type MdxJsxTextElement = { + attributes: { type: "mdxJsxAttribute"; name: string; value: string }[]; + children: PhrasingContent[]; + name: string; + type: "mdxJsxTextElement"; +}; + // Cache for parsed glossary terms let cachedTerms: GlossaryTerm[] | null = null; let cachedGlossaryPath: string | null = null; +const SPECIAL_REGEX_CHARS = /[.*+?^${}()|[\]\\]/g; + +function normalize(filePath: string): string { + return filePath.replaceAll("\\", "/"); +} + /** - * Remark plugin to automatically detect and link glossary terms + * Pages that must not get glossary links: + * - the glossary page itself (`resources/glossary.mdx` or `resources/glossary/index.mdx`) + * - the many MCP server pages under `resources/integrations/` + * - User Source per-provider pages: they discuss Microsoft, Auth0, Okta, etc. + * concepts (account, tenant, realm) that collide with Arcade glossary terms + * in ways that mislead readers. The User Sources overview itself stays opted-in. */ -export function remarkGlossary(options: RemarkGlossaryOptions) { - const { glossaryPath, caseSensitive = false } = options; +export function shouldSkipGlossary(filePath: string | undefined): boolean { + if (!filePath) { + return false; + } + const p = normalize(filePath); + if ( + p.endsWith("/resources/glossary.mdx") || + p.endsWith("/resources/glossary/index.mdx") + ) { + return true; + } + if (p.includes("/resources/integrations/")) { + return true; + } + if ( + p.includes("/operate/identity/user-sources/") && + !p.endsWith("/operate/identity/user-sources/index.mdx") + ) { + return true; + } + return false; +} - // biome-ignore lint/suspicious/noExplicitAny: unist file types are not fully typed - return (tree: Root, file: any) => { - // Don't process the glossary page itself - if (file.history?.[0]?.includes("glossary/page.mdx")) { - return; - } +function getTerms(glossaryPath: string): GlossaryTerm[] { + if (!cachedTerms || cachedGlossaryPath !== glossaryPath) { + cachedTerms = sortTermsByLength(parseGlossary(glossaryPath)); + cachedGlossaryPath = glossaryPath; + } + return cachedTerms; +} - // don't process the many MCP tool pages - if (file.history?.[0]?.includes("/resources/integrations/")) { - return; - } +function glossaryNode( + term: GlossaryTerm, + matchedText: string +): PhrasingContent { + const node: MdxJsxTextElement = { + attributes: [ + { name: "term", type: "mdxJsxAttribute", value: term.term }, + { name: "definition", type: "mdxJsxAttribute", value: term.definition }, + { name: "link", type: "mdxJsxAttribute", value: term.link }, + ], + children: [{ type: "text", value: matchedText }], + name: "GlossaryTerm", + type: "mdxJsxTextElement", + }; + // mdast's PhrasingContent union does not include MDX JSX nodes; remark-mdx adds them. + return node as unknown as PhrasingContent; +} - // Don't process User Source per-provider pages — they discuss - // Microsoft, Auth0, Okta, etc. concepts (account, tenant, realm) - // that collide with Arcade glossary terms in ways that mislead - // readers. The User Sources overview itself stays opted-in. - const filePath = file.history?.[0]; - if ( - filePath?.includes("/operate/identity/user-sources/") && - !filePath.endsWith("/operate/identity/user-sources/page.mdx") - ) { - return; - } +type Match = { + index: number; + length: number; + term: GlossaryTerm; +}; - // Lazy-load and cache glossary terms - if (!cachedTerms || cachedGlossaryPath !== glossaryPath) { - cachedTerms = sortTermsByLength(parseGlossary(glossaryPath)); - cachedGlossaryPath = glossaryPath; +/** Split `textNode` around `match`: text before, the GlossaryTerm element, text after. */ +function splitTextNode(textNode: Text, match: Match): PhrasingContent[] { + const text = textNode.value; + const beforeText = text.slice(0, match.index); + const matchedText = text.slice(match.index, match.index + match.length); + const afterText = text.slice(match.index + match.length); + + const newNodes: PhrasingContent[] = []; + if (beforeText) { + newNodes.push({ type: "text", value: beforeText }); + } + newNodes.push(glossaryNode(match.term, matchedText)); + if (afterText) { + newNodes.push({ type: "text", value: afterText }); + } + return newNodes; +} + +/** Find the first (longest-term-first) unmarked glossary term in `text`. */ +function findMatch( + text: string, + terms: GlossaryTerm[], + markedTerms: Set<string>, + caseSensitive: boolean +): Match | null { + const flags = caseSensitive ? "g" : "gi"; + for (const term of terms) { + if (markedTerms.has(term.term)) { + continue; + } + for (const alias of term.aliases) { + const pattern = new RegExp(`\\b${escapeRegex(alias)}\\b`, flags); + const match = pattern.exec(text); + if (match) { + return { index: match.index, length: match[0].length, term }; + } } + } + return null; +} + +/** + * Remark plugin to automatically detect and link glossary terms + */ +export function remarkGlossary( + options: RemarkGlossaryOptions +): (tree: Root, file: FileLike) => void { + const { glossaryPath, caseSensitive = false } = options; - const terms = cachedTerms; + return (tree: Root, file: FileLike) => { + const filePath = file.history?.[0] ?? file.path; + if (shouldSkipGlossary(filePath)) { + return; + } - // Track which terms have been marked (per paragraph) - // biome-ignore lint/suspicious/noExplicitAny: mdast node types are complex - const markedTermsPerParagraph = new Map<any, Set<string>>(); + const terms = getTerms(glossaryPath); visit(tree, "paragraph", (paragraphNode: Paragraph) => { - markedTermsPerParagraph.set(paragraphNode, new Set()); + // Track which terms have been marked in this paragraph + const markedTerms = new Set<string>(); - // Process text nodes within the paragraph visit( paragraphNode, "text", - // biome-ignore lint/suspicious/noExplicitAny: unist parent nodes are not fully typed - // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: necessary for term matching logic - (textNode: Text, index: number | undefined, parent: any) => { - // Skip if we don't have parent or index + ( + textNode: Text, + index: number | undefined, + parent: Parent | undefined + ) => { if (!parent || index === undefined) { return; } @@ -78,108 +177,26 @@ export function remarkGlossary(options: RemarkGlossaryOptions) { parent.type === "link" || parent.type === "inlineCode" || (parent.type === "mdxJsxTextElement" && + "name" in parent && parent.name === "GlossaryTerm") ) { return SKIP; } - const markedTerms = - markedTermsPerParagraph.get(paragraphNode) || new Set(); - let text = textNode.value; - let modified = false; - const newNodes: PhrasingContent[] = []; - - // Try to match terms in order of length (longest first) - for (const term of terms) { - // Skip if we've already marked this term in this paragraph - if (markedTerms.has(term.term)) { - continue; - } - - // Try each alias - for (const alias of term.aliases) { - // Build regex with word boundaries - const flags = caseSensitive ? "g" : "gi"; - const pattern = new RegExp(`\\b${escapeRegex(alias)}\\b`, flags); - const match = pattern.exec(text); - - if (match && match.index !== undefined) { - // Found a match! Split the text and insert the glossary term component - modified = true; - markedTerms.add(term.term); - - const beforeText = text.slice(0, match.index); - const matchedText = match[0]; - const afterText = text.slice(match.index + matchedText.length); - - // Add text before match - if (beforeText) { - newNodes.push({ - type: "text", - value: beforeText, - }); - } - - // Add glossary term component - newNodes.push({ - type: "mdxJsxTextElement", - name: "GlossaryTerm", - attributes: [ - { - type: "mdxJsxAttribute", - name: "term", - value: term.term, - }, - { - type: "mdxJsxAttribute", - name: "definition", - value: term.definition, - }, - { - type: "mdxJsxAttribute", - name: "link", - value: term.link, - }, - ], - children: [ - { - type: "text", - value: matchedText, - }, - ], - // biome-ignore lint/suspicious/noExplicitAny: mdxJsxTextElement not in base mdast types - } as any); - - // Continue with the remaining text - text = afterText; - - // Stop after first match for this term - break; - } - } - - // If we modified the text, stop trying other terms for this node - if (modified) { - break; - } + const match = findMatch( + textNode.value, + terms, + markedTerms, + caseSensitive + ); + if (!match) { + return; } - // If we modified the text, replace the node - if (modified) { - // Add any remaining text - if (text) { - newNodes.push({ - type: "text", - value: text, - }); - } - - // Replace the text node with our new nodes - parent.children.splice(index, 1, ...newNodes); - - // Return SKIP to avoid re-processing the nodes we just added - return SKIP; - } + markedTerms.add(match.term.term); + // Replace the text node with the split nodes and skip re-processing them + parent.children.splice(index, 1, ...splitTextNode(textNode, match)); + return SKIP; } ); }); @@ -190,5 +207,5 @@ export function remarkGlossary(options: RemarkGlossaryOptions) { * Escape special regex characters in a string */ function escapeRegex(str: string): string { - return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + return str.replace(SPECIAL_REGEX_CHARS, "\\$&"); } diff --git a/lib/remark-nextra-compat.ts b/lib/remark-nextra-compat.ts new file mode 100644 index 000000000..617a8d50c --- /dev/null +++ b/lib/remark-nextra-compat.ts @@ -0,0 +1,241 @@ +import Slugger from "github-slugger"; +import type { Root } from "mdast"; +import { visit } from "unist-util-visit"; + +/** + * Reproduces what Nextra 4 did to the Markdown tree, so the rendered HTML and + * every heading id match the Nextra-built production site: + * + * - heading ids come from one `github-slugger` per page that is shared, in + * document order, by headings (`h2`–`h6`; `h1` gets no id), tab labels and + * `<summary>` text. `## Title [#custom-id]` sets the id explicitly; + * - each `<Tabs.Tab>` starts with a hidden `<h3 id="…">label</h3>`, which is + * how tab labels reach search engines and deep links; + * - `<details>` / `<summary>` written as JSX render through the MDX component + * map (components/mdx) instead of as bare HTML elements. + * + * It must run before Fumadocs' `remarkHeading`, which keeps an id that is + * already set and builds the table of contents from it. + */ + +type JsxAttribute = { + name: string; + type: "mdxJsxAttribute"; + value?: unknown; +}; + +type JsxElement = { + attributes: JsxAttribute[]; + children: unknown[]; + data?: { _mdxExplicitJsx?: boolean }; + name: string | null; + type: "mdxJsxFlowElement" | "mdxJsxTextElement"; +}; + +type NodeLike = { + children?: NodeLike[]; + type: string; + value?: string; +}; + +const CUSTOM_ID_REGEX = /\s*\[#([\s\S]+?)]\s*$/; +const COMPONENT_HTML_TAGS = new Set(["details", "summary"]); + +function isJsxElement(node: unknown): node is JsxElement { + const type = (node as { type?: string } | null)?.type; + return type === "mdxJsxFlowElement" || type === "mdxJsxTextElement"; +} + +function flattenValue(node: NodeLike): string { + return (node.children ?? []) + .map((child) => { + if (child.children) { + return flattenValue(child); + } + return typeof child.value === "string" ? child.value : ""; + }) + .join(""); +} + +function literal(value: string | number) { + return { raw: JSON.stringify(value), type: "Literal", value }; +} + +/** `style={{ visibility: "hidden", width: 0, height: 0 }}` as an MDX attribute. */ +function hiddenStyleAttribute(): JsxAttribute { + const entries: [string, string | number][] = [ + ["visibility", "hidden"], + ["width", 0], + ["height", 0], + ]; + return { + name: "style", + type: "mdxJsxAttribute", + value: { + data: { + estree: { + body: [ + { + expression: { + properties: entries.map(([key, value]) => ({ + computed: false, + key: { name: key, type: "Identifier" }, + kind: "init", + method: false, + shorthand: false, + type: "Property", + value: literal(value), + })), + type: "ObjectExpression", + }, + type: "ExpressionStatement", + }, + ], + comments: [], + sourceType: "module", + type: "Program", + }, + }, + type: "mdxJsxAttributeValueExpression", + value: "", + }, + }; +} + +type EstreeLiteral = { + type: string; + value?: unknown; +}; + +/** Reads the string entries of `items={["a", "b"]}` on a `<Tabs>` element. */ +function tabNames(tabs: JsxElement): (string | undefined)[] | null { + const attribute = tabs.attributes.find( + (attr) => attr.type === "mdxJsxAttribute" && attr.name === "items" + ); + const expression = ( + attribute?.value as + | { + data?: { + estree?: { + body?: { expression?: { elements?: (EstreeLiteral | null)[] } }[]; + }; + }; + } + | undefined + )?.data?.estree?.body?.[0]?.expression; + if (!expression?.elements) { + return null; + } + return expression.elements.map((element) => + typeof element?.value === "string" ? element.value : undefined + ); +} + +function addSummaryIds(node: NodeLike, slugger: Slugger): void { + for (const child of node.children ?? []) { + if ( + isJsxElement(child) && + child.type === "mdxJsxTextElement" && + child.name === "summary" + ) { + child.attributes.push({ + name: "id", + type: "mdxJsxAttribute", + value: slugger.slug(flattenValue(child as unknown as NodeLike)), + }); + } else if (child.children) { + addSummaryIds(child, slugger); + } + } +} + +type HeadingLike = { + children: NodeLike[]; + data?: { hProperties?: Record<string, unknown> }; + depth: number; +}; + +/** Applies `[#custom-id]` and gives every heading below `h1` its slug id. */ +function assignHeadingId(heading: HeadingLike, slugger: Slugger): void { + heading.data ||= {}; + heading.data.hProperties ||= {}; + const props = heading.data.hProperties; + const lastChild = heading.children.at(-1); + if (lastChild?.type === "text" && typeof lastChild.value === "string") { + const matched = lastChild.value.match(CUSTOM_ID_REGEX); + if (matched) { + props.id = matched[1]; + lastChild.value = lastChild.value.slice(0, matched.index); + } + } + if (heading.depth === 1) { + return; + } + const customId = typeof props.id === "string" ? props.id : ""; + props.id = slugger.slug( + customId || flattenValue({ children: heading.children, type: "heading" }) + ); +} + +/** Prepends the hidden `<h3 id>label</h3>` to a `<Tabs.Tab>`. */ +function addTabHeading( + tab: JsxElement, + parent: unknown, + index: number | undefined, + slugger: Slugger +): void { + if (!isJsxElement(parent) || parent.name !== "Tabs" || index === undefined) { + return; + } + const name = tabNames(parent)?.[index]; + if (name === undefined) { + return; + } + const heading: JsxElement = { + attributes: [ + { name: "id", type: "mdxJsxAttribute", value: slugger.slug(name) }, + hiddenStyleAttribute(), + ], + children: [{ type: "text", value: name }], + data: { _mdxExplicitJsx: true }, + name: "h3", + type: "mdxJsxFlowElement", + }; + tab.children.unshift(heading); +} + +function visitJsxElement( + node: JsxElement, + parent: unknown, + index: number | undefined, + slugger: Slugger +): void { + if (node.name && COMPONENT_HTML_TAGS.has(node.name) && node.data) { + node.data._mdxExplicitJsx = undefined; + } + if (node.type !== "mdxJsxFlowElement") { + return; + } + if (node.name === "Tabs.Tab") { + addTabHeading(node, parent, index, slugger); + } + if (node.name === "details") { + addSummaryIds(node as unknown as NodeLike, slugger); + } +} + +export function remarkNextraCompat(): (tree: Root) => void { + return (tree: Root) => { + const slugger = new Slugger(); + + visit(tree, (node, index, parent) => { + if (node.type === "heading") { + assignHeadingId(node as unknown as HeadingLike, slugger); + return; + } + if (isJsxElement(node)) { + visitJsxElement(node, parent, index, slugger); + } + }); + }; +} diff --git a/lib/remark-smartypants.ts b/lib/remark-smartypants.ts new file mode 100644 index 000000000..69b6b0860 --- /dev/null +++ b/lib/remark-smartypants.ts @@ -0,0 +1,190 @@ +import type { Root } from "mdast"; +import { visit } from "unist-util-visit"; + +/** + * Typographic punctuation, as Nextra applied it through `remark-smartypants` + * (retext-smartypants): straight quotes become curly, `--` an em dash and + * `...` an ellipsis, in prose only (never in code). Ported here because the + * production pages are rendered with it and the package is not a dependency. + * + * Like the original, quotes are resolved over the text of the whole page + * joined together, so a quote that opens before a link or bold run and closes + * after it is paired correctly; inline code counts as a word. + */ + +type TokenType = "word" | "space" | "punctuation"; + +type Token = { + type: TokenType; + value: string; +}; + +type Literal = { + type: string; + value: string; +}; + +const WORD_CHAR = /[\p{L}\p{N}\p{M}]/u; +const SPACE_CHAR = /\s/u; +const DECADE = /^\d\ds$/; +const DASHES = /(?<!-)--(?!-)/g; +const ELLIPSIS = /(?<!\.)\.\.\.(?!\.)/g; +const SENTENCE_END = new Set([".", "?", "!"]); +const IGNORED_JSX_PARENTS = new Set(["style", "script"]); +const OPENING = { "'": "‘", '"': "“" } as const; +const CLOSING = { "'": "’", '"': "”" } as const; + +function charType(char: string): TokenType { + if (WORD_CHAR.test(char)) { + return "word"; + } + return SPACE_CHAR.test(char) ? "space" : "punctuation"; +} + +function tokenize(text: string): Token[] { + const tokens: Token[] = []; + for (const char of text) { + const type = charType(char); + const last = tokens.at(-1); + if (last && last.type === type && type !== "punctuation") { + last.value += char; + } else { + tokens.push({ type, value: char }); + } + } + return tokens; +} + +function isQuote(value: string | undefined): value is '"' | "'" { + return value === '"' || value === "'"; +} + +/** The retext-smartypants quote rules, in their original order. */ +function educateQuote(tokens: Token[], index: number): void { + const node = tokens[index]; + const value = node?.value; + if (!(node && isQuote(value))) { + return; + } + const previous = tokens[index - 1]; + const next = tokens[index + 1]; + const nextNext = tokens[index + 2]; + + if ( + next && + nextNext && + next.type === "punctuation" && + nextNext.type !== "word" + ) { + node.value = CLOSING[value]; + } else if ( + next && + nextNext && + isQuote(next.value) && + nextNext.type === "word" + ) { + node.value = OPENING[value]; + next.value = OPENING[next.value]; + } else if (next && DECADE.test(next.value)) { + node.value = CLOSING[value]; + } else { + node.value = closesByContext(value, previous, next) + ? CLOSING[value] + : OPENING[value]; + } +} + +/** The rules that look at the token before the quote, in their original order. */ +function closesByContext( + value: '"' | "'", + previous: Token | undefined, + next: Token | undefined +): boolean { + if (previous && SENTENCE_END.has(previous.value)) { + // A quote right after sentence-ending punctuation closes it. retext sees + // this through sentence boundaries; the joined text has none where a + // table cell or heading follows the paragraph. + return true; + } + if (previous && previous.type !== "word" && next?.type === "word") { + return false; + } + if (previous && previous.type === "word") { + return true; + } + return ( + !next || next.type === "space" || (value === "'" && next.value === "s") + ); +} + +export function educateQuotes(text: string): string { + const tokens = tokenize(text); + for (let index = 0; index < tokens.length; index += 1) { + educateQuote(tokens, index); + } + return tokens.map((token) => token.value).join(""); +} + +/** + * The same punctuation for a standalone string, such as a frontmatter title + * rendered as the page heading: production's heading was part of the MDX body. + */ +export function smartypants(text: string): string { + return educateQuotes(text).replace(DASHES, "—").replace(ELLIPSIS, "…"); +} + +function isVisited(node: { type: string }, parent: unknown): boolean { + if (!parent) { + return false; + } + const { type, name } = parent as { type?: string; name?: unknown }; + if ( + type === "mdxJsxTextElement" && + !(typeof name === "string" && !IGNORED_JSX_PARENTS.has(name)) + ) { + return false; + } + return ( + node.type === "text" || + node.type === "inlineCode" || + node.type === "paragraph" + ); +} + +export function remarkSmartypants(): (tree: Root) => void { + return (tree: Root) => { + let allText = ""; + const nodes: { type: string; value?: string }[] = []; + + visit(tree, (node, _index, parent) => { + if (!isVisited(node, parent)) { + return; + } + if (node.type === "paragraph") { + // A separator, so a quote that starts a paragraph reads as opening. + allText += " "; + } else { + const { value } = node as Literal; + allText += node.type === "text" ? value : "A".repeat(value.length); + } + nodes.push(node as { type: string; value?: string }); + }); + + const educated = educateQuotes(allText); + let start = 0; + for (const node of nodes) { + if (node.type === "paragraph" || node.value === undefined) { + start += 1; + continue; + } + const end = start + node.value.length; + if (node.type === "text") { + node.value = educated + .slice(start, end) + .replace(DASHES, "—") + .replace(ELLIPSIS, "…"); + } + start = end; + } + }; +} diff --git a/lib/source.ts b/lib/source.ts new file mode 100644 index 000000000..3cc78fb75 --- /dev/null +++ b/lib/source.ts @@ -0,0 +1,35 @@ +import { type LoaderPlugin, loader } from "fumadocs-core/source"; +import { lucideIconsPlugin } from "fumadocs-core/source/plugins/lucide-icons"; +import { docs } from "@/.source/server"; + +/** + * A page can have a sidebar label that differs from its H1 (e.g. index pages + * labelled "Overview"). The label lives in the page frontmatter as + * `sidebarTitle`; this plugin shows it in the sidebar while the page keeps + * rendering `title`. + */ +const sidebarTitlePlugin: LoaderPlugin = { + name: "arcade:sidebar-title", + transformPageTree: { + file(node, filePath) { + if (!filePath) { + return node; + } + const file = this.storage.read(filePath); + if (file?.format !== "page") { + return node; + } + const { sidebarTitle } = file.data as { sidebarTitle?: unknown }; + if (typeof sidebarTitle === "string" && sidebarTitle.trim()) { + return { ...node, name: sidebarTitle }; + } + return node; + }, + }, +}; + +export const source = loader({ + baseUrl: "/en", + plugins: [lucideIconsPlugin(), sidebarTitlePlugin], + source: docs.toFumadocsSource(), +}); diff --git a/lib/swc-page-extensions.ts b/lib/swc-page-extensions.ts deleted file mode 100644 index 91d668880..000000000 --- a/lib/swc-page-extensions.ts +++ /dev/null @@ -1,64 +0,0 @@ -const MARKDOWN_PAGE_EXTENSIONS = new Set(["md", "mdx"]); - -const NESTED_RULE_KEYS = ["use", "oneOf", "rules"] as const; - -type SwcLoaderOptions = { - nextConfig?: { pageExtensions?: string[] }; -}; - -/** - * Next 16.2 started building the App Router "is this a page/layout/route?" - * check inside the SWC react-server-components transform out of - * `nextConfig.pageExtensions`. Next 16.1 and earlier hard-coded that check as - * `/(page|layout|route)\.(ts|js)x?$/`, so a `page.mdx` never matched it. - * - * Nextra adds "md" and "mdx" to `pageExtensions` so its MDX files route, which - * means every `page.mdx` now reaches a validator that had never seen one. On - * the client layer that validator treats any app entry as a client entry and - * rejects the `export const metadata` Nextra generates on every page, so the - * whole site fails to compile. - * - * Inside SWC, `pageExtensions` feeds nothing but that regex, and it gets there - * only through next-swc-loader. Dropping the markdown extensions from the copy - * handed to the loader therefore restores the 16.1 behavior for MDX pages and - * leaves routing — which reads the real `nextConfig.pageExtensions` — alone. - * The metadata still resolves on the server; see tests/mdx-page-metadata.test.ts. - * - * Remove once https://github.com/shuding/nextra/issues/5003 is fixed upstream. - */ -export function stripMarkdownFromSwcPageExtensions(rules: unknown): void { - if (Array.isArray(rules)) { - for (const rule of rules) { - stripMarkdownFromSwcPageExtensions(rule); - } - return; - } - if (!rules || typeof rules !== "object") { - return; - } - - const rule = rules as Record<string, unknown>; - const loader = rule.loader; - const options = rule.options as SwcLoaderOptions | undefined; - - if ( - typeof loader === "string" && - loader.includes("next-swc-loader") && - options?.nextConfig?.pageExtensions - ) { - // Replace rather than splice: the same nextConfig object is shared across - // every loader entry, and routing still needs its markdown extensions. - options.nextConfig = { - ...options.nextConfig, - pageExtensions: options.nextConfig.pageExtensions.filter( - (extension) => !MARKDOWN_PAGE_EXTENSIONS.has(extension) - ), - }; - } - - for (const key of NESTED_RULE_KEYS) { - if (rule[key]) { - stripMarkdownFromSwcPageExtensions(rule[key]); - } - } -} diff --git a/app/_lib/toolkit-data.ts b/lib/toolkit-data.ts similarity index 99% rename from app/_lib/toolkit-data.ts rename to lib/toolkit-data.ts index badf0e5c6..dc1a8a9b2 100644 --- a/app/_lib/toolkit-data.ts +++ b/lib/toolkit-data.ts @@ -6,7 +6,7 @@ import type { ToolkitData, ToolkitSummary, ToolSummary, -} from "@/app/_components/toolkit-docs/types"; +} from "@/components/toolkit-docs/types"; import { resolveToolkitDataDir } from "@/toolkit-docs-generator/src/shared/toolkit-data-dir"; import { getToolkitSlug, diff --git a/app/_lib/toolkit-markdown.ts b/lib/toolkit-markdown.ts similarity index 98% rename from app/_lib/toolkit-markdown.ts rename to lib/toolkit-markdown.ts index 1c4629c9a..36dd01c44 100644 --- a/app/_lib/toolkit-markdown.ts +++ b/lib/toolkit-markdown.ts @@ -2,7 +2,7 @@ import type { ToolDefinition, ToolkitData, ToolParameter, -} from "@/app/_components/toolkit-docs/types"; +} from "@/components/toolkit-docs/types"; /** * Serialize full toolkit data to markdown for the "copy page as markdown" / diff --git a/lib/toolkit-route.tsx b/lib/toolkit-route.tsx new file mode 100644 index 000000000..1df3455b4 --- /dev/null +++ b/lib/toolkit-route.tsx @@ -0,0 +1,154 @@ +/** + * Toolkit (MCP server) pages are not MDX files: they are rendered from the JSON + * data in toolkit-docs-generator/data/toolkits. The docs catch-all route + * (app/en/[[...slug]]/page.tsx) calls `source.getPage(slug)` first and falls + * back to this module when no MDX page matches the slug: + * + * matchToolkitRoute(slug) -> { category, toolkitId } | null + * ToolkitRoutePage(match) -> rendered page, or null when no toolkit exists + * + * `generateToolkitParams` feeds `generateStaticParams` so every toolkit page is + * prerendered; `toolkitRouteMetadata` builds the per-toolkit metadata (title, + * description, canonical, and `noindex` for hidden toolkits). + */ +import { DocsPage } from "fumadocs-ui/layouts/notebook/page"; +import type { Metadata } from "next"; +import type { ReactNode } from "react"; +import { ToolkitPage } from "@/components/toolkit-docs"; +import { source } from "@/lib/source"; +import { readToolkitData, toToolkitSummary } from "@/lib/toolkit-data"; +import { + getToolkitCanonicalPath, + getToolkitStaticParamsForCategory, +} from "@/lib/toolkit-static-params"; +import { INTEGRATION_CATEGORIES } from "@/toolkit-docs-generator/src/shared/toolkit-primitives"; + +export type ToolkitRouteMatch = { + category: string; + toolkitId: string; +}; + +const ROUTE_PREFIX = ["resources", "integrations"] as const; +const ROUTE_LENGTH = ROUTE_PREFIX.length + 2; + +/** Returns a match when `slug` looks like resources/integrations/<category>/<toolkitId>. */ +export function matchToolkitRoute( + slug: string[] | undefined +): ToolkitRouteMatch | null { + if (!slug || slug.length !== ROUTE_LENGTH) { + return null; + } + const [a, b, category, toolkitId] = slug; + if ( + a !== ROUTE_PREFIX[0] || + b !== ROUTE_PREFIX[1] || + !category || + !toolkitId + ) { + return null; + } + return { category, toolkitId }; +} + +const toSlug = (match: ToolkitRouteMatch): string[] => [ + ...ROUTE_PREFIX, + match.category, + match.toolkitId, +]; + +/** + * Static params for every generated toolkit page, as `{ slug: string[] }`. + * + * Authored MDX pages that live under the same category folders (the partner + * pages `search/nimble` and `search/tavily`) are served by the MDX branch of + * the catch-all route, so a toolkit whose slug collides with one is dropped + * here rather than emitted twice. + */ +export async function generateToolkitParams(): Promise<{ slug: string[] }[]> { + const perCategory = await Promise.all( + INTEGRATION_CATEGORIES.map(async (category) => { + const params = await getToolkitStaticParamsForCategory(category); + return params.map(({ toolkitId }) => ({ category, toolkitId })); + }) + ); + + return perCategory + .flat() + .map(toSlug) + .filter((slug) => source.getPage(slug) === undefined) + .map((slug) => ({ slug })); +} + +/** Metadata for a toolkit page, or null when the toolkit does not exist. */ +export async function toolkitRouteMetadata( + match: ToolkitRouteMatch +): Promise<Metadata | null> { + const data = await readToolkitData(match.toolkitId); + if (!data) { + return null; + } + + // Canonicalize to the toolkit's own category + slug, not the URL it was + // reached through. The route accepts any category segment, so a + // wrong-category alias (e.g. development/pagerduty-api for a customer-support + // toolkit) must point at the one generated, index-linked page. + const canonical = getToolkitCanonicalPath({ + category: data.metadata?.category, + docsLink: data.metadata?.docsLink, + id: data.id, + }); + + const metadata: Metadata = { + alternates: { canonical }, + description: data.description || "Generated MCP server documentation.", + title: data.label || data.id, + }; + + // Hidden toolkits stay reachable via the route (and render as non-clickable + // cards in the index), but must not be indexed — otherwise their + // self-canonical is flagged as an orphan with no incoming links. + if (data.metadata?.isHidden) { + metadata.robots = { index: false }; + } + + return metadata; +} + +/** + * URL the "Copy page" action requests for a toolkit page. Sent with + * `Accept: text/markdown`, the data route answers with the full page as + * Markdown (the page itself loads per-tool detail on demand, so its HTML does + * not contain it). + */ +export function toolkitMarkdownUrl(match: ToolkitRouteMatch): string { + return `/api/toolkit-data/${encodeURIComponent(match.toolkitId)}`; +} + +/** + * Renders the full toolkit page (DocsPage chrome included), or null when the + * toolkit does not exist. `ToolkitPage` is a client component that ships its + * own "On this page" sidebar and scroll controls, so Fumadocs' table of + * contents is disabled and the page takes the full content width. + */ +export async function ToolkitRoutePage(props: { + match: ToolkitRouteMatch; +}): Promise<ReactNode | null> { + const data = await readToolkitData(props.match.toolkitId); + if (!data) { + return null; + } + + // Pass a summary (per-tool detail stripped) so the heavy fields never enter + // the initial Flight payload — detail is fetched on expand. See MARTECH-17. + return ( + <DocsPage + // ToolkitPage renders its own breadcrumb and table of contents. + breadcrumb={{ enabled: false }} + full + tableOfContent={{ enabled: false }} + toc={[]} + > + <ToolkitPage data={toToolkitSummary(data)} /> + </DocsPage> + ); +} diff --git a/app/_lib/toolkit-slug.ts b/lib/toolkit-slug.ts similarity index 100% rename from app/_lib/toolkit-slug.ts rename to lib/toolkit-slug.ts diff --git a/app/_lib/toolkit-static-params.ts b/lib/toolkit-static-params.ts similarity index 75% rename from app/_lib/toolkit-static-params.ts rename to lib/toolkit-static-params.ts index d93bdd8f1..bcae20c87 100644 --- a/app/_lib/toolkit-static-params.ts +++ b/lib/toolkit-static-params.ts @@ -1,3 +1,4 @@ +import type { Dirent } from "node:fs"; import { readdir } from "node:fs/promises"; import { join } from "node:path"; import { TOOLKITS as DESIGN_SYSTEM_TOOLKITS } from "@arcadeai/design-system/metadata/toolkits"; @@ -69,7 +70,7 @@ export function normalizeCategory( throw new Error( `Unrecognized integration category "${value}". Expected one of: ${INTEGRATION_CATEGORIES.join(", ")}. ` + - "A new category needs a matching app/en/resources/integrations/<category>/[toolkitId] route directory before toolkits can use it." + "A new category needs a matching content/docs/resources/integrations/<category>/meta.json sidebar entry before toolkits can use it." ); } @@ -111,7 +112,7 @@ const listToolkitRoutesFromDataDir = async (options?: { const dataDir = resolveDataDir(options?.dataDir); // loadAllToolkitData validates every file against MergedToolkitSchema and - // throws on a corrupt one (see app/_lib/toolkit-data.ts) — a malformed file + // throws on a corrupt one (see lib/toolkit-data.ts) — a malformed file // in this directory listing is never legitimately "absent", so it should // fail the build rather than be skipped here. const { byNormalizedId } = await loadAllToolkitData(dataDir); @@ -194,13 +195,15 @@ export async function listToolkitRoutes(options?: { toolkitsCatalog.map((toolkit) => [normalizeToolkitId(toolkit.id), toolkit]) ); + // Resolve concurrently, then insert in index order so the last toolkit for + // a given slug still wins, as it did when this loop was sequential. + const routes = await Promise.all( + index.toolkits.map((toolkit) => + resolveToolkitRoute(toolkit, catalogByNormalizedId, options?.dataDir) + ) + ); const unique = new Map<string, ToolkitRouteEntry>(); - for (const toolkit of index.toolkits) { - const route = await resolveToolkitRoute( - toolkit, - catalogByNormalizedId, - options?.dataDir - ); + for (const route of routes) { if (!route) { continue; } @@ -221,42 +224,79 @@ export async function getToolkitStaticParamsForCategory( .map((route) => ({ toolkitId: route.toolkitId })); } -const INTEGRATIONS_APP_DIR = join( +const INTEGRATIONS_CONTENT_DIR = join( process.cwd(), - "app", - "en", + "content", + "docs", "resources", "integrations" ); -const PAGE_FILE_NAMES = new Set(["page.mdx", "page.tsx"]); +const MDX_EXTENSION_REGEX = /\.mdx?$/; /** - * Authored static integration pages (e.g. partner pages like `search/tavily` - * and `tool-feedback`) live next to the dynamic `[toolkitId]` routes. They are - * real pages but are not part of `listToolkitRoutes`, so enumerate them from - * disk under the known integration categories. + * Authored static integration pages (e.g. partner pages like `search/tavily`) + * live as MDX files next to the generated category sidebar (`meta.json`) under + * `content/docs/resources/integrations/<category>/`. They are real pages but + * are not part of `listToolkitRoutes`, so enumerate them from disk under the + * known integration categories. Both `<slug>.mdx` and `<slug>/index.mdx` are + * recognized; the category's own `index.mdx` is not a toolkit-level page. */ -const listStaticIntegrationLinks = async (): Promise<string[]> => { - const links: string[] = []; - - for (const category of INTEGRATION_CATEGORIES) { - const categoryDir = join(INTEGRATIONS_APP_DIR, category); - try { - const slugs = await readdir(categoryDir, { withFileTypes: true }); - for (const slug of slugs) { - if (!slug.isDirectory() || slug.name.startsWith("[")) { - continue; - } - const files = await readdir(join(categoryDir, slug.name)); - if (files.some((file) => PAGE_FILE_NAMES.has(file))) { - links.push(`/en/resources/integrations/${category}/${slug.name}`); - } - } - } catch { - // Category dir missing or unreadable — skip it. - } +const hasIndexMdx = (files: string[]): boolean => + files.some( + (file) => + MDX_EXTENSION_REGEX.test(file) && + file.replace(MDX_EXTENSION_REGEX, "") === "index" + ); + +const resolveStaticIntegrationLink = async ( + category: string, + categoryDir: string, + entry: Dirent +): Promise<string | null> => { + if (entry.isDirectory()) { + const files = await readdir(join(categoryDir, entry.name)); + return hasIndexMdx(files) + ? `/en/resources/integrations/${category}/${entry.name}` + : null; + } + if (!(entry.isFile() && MDX_EXTENSION_REGEX.test(entry.name))) { + return null; + } + const slug = entry.name.replace(MDX_EXTENSION_REGEX, ""); + if (slug === "index") { + return null; } + return `/en/resources/integrations/${category}/${slug}`; +}; + +const listCategoryIntegrationLinks = async ( + category: string +): Promise<string[]> => { + const categoryDir = join(INTEGRATIONS_CONTENT_DIR, category); + try { + const entries = await readdir(categoryDir, { withFileTypes: true }); + const resolved = await Promise.all( + entries.map((entry) => + resolveStaticIntegrationLink(category, categoryDir, entry) + ) + ); + return resolved.filter((link): link is string => link !== null); + } catch { + // Category dir missing or unreadable — skip it. + return []; + } +}; + +const listStaticIntegrationLinks = async (): Promise<string[]> => { + // Categories are read concurrently; the result keeps category order and, + // within a category, directory-entry order (same as the sequential version). + const perCategory = await Promise.all( + INTEGRATION_CATEGORIES.map((category) => + listCategoryIntegrationLinks(category) + ) + ); + const links = perCategory.flat(); return links; }; diff --git a/app/_lib/utils.ts b/lib/utils.ts similarity index 100% rename from app/_lib/utils.ts rename to lib/utils.ts diff --git a/mdx-components.ts b/mdx-components.ts deleted file mode 100644 index 8c466f8a4..000000000 --- a/mdx-components.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { UseMDXComponents } from "nextra/mdx-components"; -import { useMDXComponents as getDocsMdxComponents } from "nextra-theme-docs"; -import CustomPre from "./app/_components/custom-pre"; -import { DashboardLink } from "./app/_components/dashboard-link"; -import { GlossaryTerm } from "./app/_components/glossary-term"; -import { GuideOverview } from "./app/_components/guide-overview"; - -const docsComponents = getDocsMdxComponents({ - pre: CustomPre, -}); - -export const useMDXComponents: UseMDXComponents<typeof docsComponents> = <T>( - components?: T -) => ({ - ...docsComponents, - DashboardLink, - GlossaryTerm, - GuideOverview, - ...components, -}); diff --git a/mdx-components.tsx b/mdx-components.tsx new file mode 100644 index 000000000..45c1b6ef6 --- /dev/null +++ b/mdx-components.tsx @@ -0,0 +1,124 @@ +import defaultMdxComponents from "fumadocs-ui/mdx"; +import type { MDXComponents } from "mdx/types"; +import Image from "next/image"; +import { AgentFrameworkTabs } from "@/components/agent-framework-tabs"; +import { SignupLink } from "@/components/analytics"; +import { + CheatSheetGrid, + CheatSheetSection, + InfoBox, +} from "@/components/cheat-sheet"; +import { ContactEmail } from "@/components/contact-email"; +import { DashboardLink } from "@/components/dashboard-link"; +import { ErrorHierarchy } from "@/components/error-hierarchy"; +import { GlossaryTerm } from "@/components/glossary-term"; +import { GuideOverview } from "@/components/guide-overview"; +import { Callout } from "@/components/mdx/callout"; +import { Card, Cards } from "@/components/mdx/cards"; +import { Details } from "@/components/mdx/details"; +import { + Blockquote, + Code, + H1, + H2, + H3, + H4, + H5, + H6, + Link, + ListItem, + OrderedList, + Paragraph, + Rule, + Summary, + Table, + TableWithParts, + Td, + Th, + Tr, + UnorderedList, + Wrapper, +} from "@/components/mdx/elements"; +import { ImageZoom } from "@/components/mdx/image-zoom"; +import { Mermaid } from "@/components/mdx/mermaid"; +import { Pre } from "@/components/mdx/pre"; +import { Step, Steps } from "@/components/mdx/steps"; +import { Tabs } from "@/components/mdx/tabs"; +import { Tab } from "@/components/mdx/tabs.client"; +import { SampleAppCard } from "@/components/sample-app-card"; +import { SubpageList } from "@/components/subpage-list"; +import ToggleContent from "@/components/toggle-content"; +import { ToolCard } from "@/components/tool-card"; + +/** + * Components available in every MDX page without an import. + * + * The lowercase entries are the Nextra docs theme's element renderers, ported + * in components/mdx so the article looks exactly like the production site. + * They are applied after `components`: the page passes Fumadocs' + * relative-file `a` renderer, which the docs do not use (every link is an + * absolute path) and which would drop the Nextra link style and the + * external-link arrow. + * + * `MCPClientGrid` (components/pages/get-started/mcp-clients/mcp-client-grid.tsx) + * is page-local and imported explicitly by the page that uses it. + */ +export function getMDXComponents(components?: MDXComponents): MDXComponents { + return { + ...defaultMdxComponents, + // Arcade components + AgentFrameworkTabs, + // Nextra-compat components + Callout, + Card, + Cards, + CheatSheetGrid, + CheatSheetSection, + Code, + ContactEmail, + DashboardLink, + ErrorHierarchy, + GlossaryTerm, + GuideOverview, + Image, + InfoBox, + Mermaid, + SampleAppCard, + SignupLink, + Step, + Steps, + SubpageList, + Tab, + Table: TableWithParts, + Tabs, + ToggleContent, + ToolCard, + ...components, + // Nextra element renderers + a: Link, + blockquote: Blockquote, + code: Code, + details: Details, + h1: H1, + h2: H2, + h3: H3, + h4: H4, + h5: H5, + h6: H6, + hr: Rule, + img: ImageZoom, + li: ListItem, + ol: OrderedList, + p: Paragraph, + pre: Pre, + summary: Summary, + table: Table, + td: Td, + th: Th, + tr: Tr, + ul: UnorderedList, + wrapper: Wrapper, + }; +} + +export const useMDXComponents = getMDXComponents; diff --git a/next.config.ts b/next.config.ts index c6a53a2a5..219111e26 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,49 +1,10 @@ +import { createMDX } from "fumadocs-mdx/next"; import type { NextConfig } from "next"; -import nextra from "nextra"; -import { withLlmsTxt } from "./lib/next-plugin-llmstxt"; -import { remarkGlossary } from "./lib/remark-glossary"; -import { stripMarkdownFromSwcPageExtensions } from "./lib/swc-page-extensions"; import { redirects } from "./redirects"; -// Set up Nextra with its configuration -const withNextra = nextra({ - defaultShowCopyCode: true, - codeHighlight: true, - search: false, - mdxOptions: { - remarkPlugins: [ - [ - remarkGlossary, - { glossaryPath: "./app/en/resources/glossary/page.mdx" }, - ], - ], - }, -}); - -const nextConfig: NextConfig = withLlmsTxt({ - enabled: false, // disabled for now, we will recreate this every week -})( - withNextra({ - async redirects() { - return redirects; - }, - // The app imports shared modules out of toolkit-docs-generator/src/shared/, - // which compiles under "moduleResolution": "NodeNext" and therefore writes - // its internal relative imports with a ".js" extension. Webpack resolves - // with bundler semantics and would look for a literal ".js" file that - // never exists on disk, so teach it to try ".ts" first. - webpack: (config) => { - config.resolve.extensionAlias = { - ...config.resolve.extensionAlias, - ".js": [".ts", ".tsx", ".js"], - }; - // Keeps `page.mdx` out of Next 16.2's new SWC app-entry check, which - // otherwise rejects the `metadata` export Nextra generates. See - // lib/swc-page-extensions.ts for the full story. - stripMarkdownFromSwcPageExtensions(config.module.rules); - return config; - }, - headers: async () => [ +const config: NextConfig = { + headers: () => + Promise.resolve([ { source: "/(.*)", headers: [ @@ -65,8 +26,14 @@ const nextConfig: NextConfig = withLlmsTxt({ }, ], }, - ], - }) -); + ]), + reactStrictMode: true, + // Redirect rules for renamed, merged, or deleted pages live in redirects.ts + // (a plain data module) so scripts/check-redirects.ts and the tests can + // import them without parsing this file. + redirects: () => Promise.resolve(redirects), +}; + +const withMDX = createMDX(); -export default nextConfig; +export default withMDX(config); diff --git a/package.json b/package.json index 4e30522b2..a5ccb7524 100644 --- a/package.json +++ b/package.json @@ -1,57 +1,59 @@ { "name": "arcade-docs", - "version": "0.0.1", - "description": "Arcade Documentation", + "version": "0.1.0", + "private": true, + "description": "Arcade Documentation (Fumadocs)", + "license": "MIT", "type": "module", "scripts": { - "dev": "next dev --webpack", - "build": "next build --webpack", + "build": "next build", + "check-meta": "bun scripts/check-meta-keys.ts", + "check-redirects": "bun scripts/check-redirects.ts", + "dev": "next dev", + "format": "bunx ultracite fix", + "postinstall": "fumadocs-mdx", + "lint": "bunx ultracite check", + "llmstxt": "tsx scripts/generate-llmstxt.ts", + "metadata-report": "bun toolkit-docs-generator/scripts/report-tool-metadata.ts", + "prepare": "husky", "start": "next start", - "lint": "pnpm exec ultracite check", - "format": "pnpm exec ultracite fix", + "sync-crawler-config": "bun scripts/sync-crawler-config.ts", + "sync-toolkit-sidebar": "tsx toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts", + "test": "bunx vitest --run", + "test:external-urls": "bunx vitest run --config vitest.external-urls.config.ts", + "test:watch": "bunx vitest --watch", "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p scripts/tsconfig.json --noEmit && tsc -p toolkit-docs-generator/tsconfig.json --noEmit", - "prepare": "husky install", - "llmstxt": "pnpm exec tsx scripts/generate-llmstxt.ts", - "test": "vitest --run", - "test:watch": "vitest --watch", + "update-links": "bun scripts/update-internal-links.ts", "vale": "vale", - "vale:check": "vale app/en/", - "vale:fix": "pnpm exec tsx scripts/vale-fix.ts", - "vale:review": "pnpm exec tsx scripts/vale-style-review.ts", - "vale:editorial": "pnpm exec tsx scripts/vale-editorial.ts", - "vale:sync": "vale sync", - "check-redirects": "pnpm exec tsx scripts/check-redirects.ts", - "update-links": "pnpm exec tsx scripts/update-internal-links.ts", - "check-meta": "pnpm exec tsx scripts/check-meta-keys.ts", - "sync-crawler-config": "pnpm exec tsx scripts/sync-crawler-config.ts", - "metadata-report": "pnpm exec tsx toolkit-docs-generator/scripts/report-tool-metadata.ts" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/ArcadeAI/arcade-ai.git" + "vale:check": "vale content/docs/", + "vale:editorial": "bun scripts/vale-editorial.ts", + "vale:fix": "bun scripts/vale-fix.ts", + "vale:review": "bun scripts/vale-style-review.ts", + "vale:sync": "vale sync" }, - "authors": [ - "Sam Partee <sam@arcade.dev>", - "Nate Barbettini <nate@arcade.dev>" - ], - "license": "MIT", - "bugs": { - "url": "https://github.com/ArcadeAI/arcade-ai/issues" + "lint-staged": { + "!(examples)/**/*.{js,jsx,ts,tsx,json,jsonc,css,scss,md,mdx}": [ + "bunx ultracite fix" + ] }, - "homepage": "https://arcade.dev/", "dependencies": { "@arcadeai/design-system": "7.11.0", "@arcadeai/ui-kit": "0.14.0", "@mdx-js/mdx": "3.1.1", "@next/third-parties": "16.3.8", "@ory/client": "1.22.37", + "@shikijs/transformers": "4.5.0", "@uidotdev/usehooks": "2.4.1", "algoliasearch": "5.53.0", + "fumadocs-core": "16.15.18", + "fumadocs-mdx": "15.4.6", + "fumadocs-ui": "16.15.18", + "github-slugger": "2.0.0", "lucide-react": "0.577.0", + "mermaid": "11.17.2", "motion": "12.40.0", "next": "16.3.8", - "nextra": "4.6.1", - "nextra-theme-docs": "4.6.1", + "next-themes": "0.4.6", "posthog-js": "1.379.0", "posthog-node": "5.35.12", "react": "19.2.7", @@ -73,6 +75,7 @@ "@octokit/rest": "22.0.1", "@tailwindcss/postcss": "4.3.0", "@tailwindcss/typography": "0.5.19", + "@types/bun": "1.4.2", "@types/hast": "3.0.5", "@types/mdast": "4.0.4", "@types/mdx": "2.0.13", @@ -87,6 +90,7 @@ "fast-glob": "3.3.3", "husky": "9.1.7", "lint-staged": "16.4.0", + "mdast-util-to-string": "4.0.0", "next-validate-link": "1.6.6", "openai": "6.41.0", "ora": "9.4.0", @@ -98,14 +102,28 @@ "vite": "7.3.5", "vitest": "4.1.8" }, + "overrides": { + "postcss": "8.5.26", + "@xmldom/xmldom": ">=0.9.12", + "esbuild": "0.28.1", + "form-data": "4.0.6", + "dompurify": ">=3.4.13 <4", + "protobufjs": ">=7.6.3 <8", + "axios": ">=1.19.0 <2", + "brace-expansion": ">=5.0.12 <6", + "seroval": "1.6.8", + "seroval-plugins": "1.6.8", + "source-map-js": "1.2.2", + "sharp": "0.35.5", + "js-yaml": ">=4.3.1 <5", + "@opentelemetry/core": ">=2.8.0 <2.11.0", + "nanoid": ">=3.3.18 <4", + "fflate": ">=0.4.9 <0.5", + "qs": ">=6.16.0 <7" + }, + "packageManager": "bun@1.4.2", "engines": { "node": "22.x", - "pnpm": ">=9.15.4" - }, - "lint-staged": { - "!(examples)/**/*.{js,jsx,ts,tsx,json,jsonc,css,scss,md,mdx}": [ - "pnpm exec ultracite fix" - ] - }, - "packageManager": "pnpm@10.11.0" + "bun": ">=1.4.0" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml deleted file mode 100644 index ac5dada15..000000000 --- a/pnpm-lock.yaml +++ /dev/null @@ -1,10068 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -overrides: - postcss: 8.5.26 - '@xmldom/xmldom': '>=0.9.12' - esbuild: 0.28.1 - form-data: 4.0.6 - dompurify: '>=3.4.13 <4' - protobufjs: '>=7.6.3 <8' - axios: '>=1.19.0 <2' - brace-expansion: '>=5.0.12 <6' - seroval: 1.6.8 - seroval-plugins: 1.6.8 - source-map-js: 1.2.2 - sharp: 0.35.5 - js-yaml: '>=4.3.1 <5' - '@opentelemetry/core': '>=2.8.0 <2.11.0' - mermaid: '>=11.16.1 <12' - nanoid: '>=3.3.18 <4' - fflate: '>=0.4.9 <0.5' - qs: '>=6.16.0 <7' - -importers: - - .: - dependencies: - '@arcadeai/design-system': - specifier: 7.11.0 - version: 7.11.0(@date-fns/tz@1.4.1)(@hookform/resolvers@5.2.2(react-hook-form@7.77.0(react@19.2.7)))(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(lucide-react@0.577.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react-hook-form@7.77.0(react@19.2.7))(react@19.2.7)(recharts@3.6.0(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react-is@16.13.1)(react@19.2.7)(redux@5.0.1))(tailwindcss@4.3.0) - '@arcadeai/ui-kit': - specifier: 0.14.0 - version: 0.14.0(@ai-sdk/react@4.0.40(react@19.2.7)(zod@4.3.6))(@arcadeai/design-system@7.11.0(@date-fns/tz@1.4.1)(@hookform/resolvers@5.2.2(react-hook-form@7.77.0(react@19.2.7)))(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(lucide-react@0.577.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react-hook-form@7.77.0(react@19.2.7))(react@19.2.7)(recharts@3.6.0(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react-is@16.13.1)(react@19.2.7)(redux@5.0.1))(tailwindcss@4.3.0))(@tanstack/react-query@5.101.4(react@19.2.7))(@tanstack/react-router@1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(ai@7.0.37(zod@4.3.6))(lucide-react@0.577.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(tailwindcss@4.3.0) - '@mdx-js/mdx': - specifier: 3.1.1 - version: 3.1.1 - '@next/third-parties': - specifier: 16.3.8 - version: 16.3.8(next@16.3.8(@opentelemetry/api@1.9.0)(@types/node@22.19.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) - '@ory/client': - specifier: 1.22.37 - version: 1.22.37 - '@uidotdev/usehooks': - specifier: 2.4.1 - version: 2.4.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - algoliasearch: - specifier: 5.53.0 - version: 5.53.0 - lucide-react: - specifier: 0.577.0 - version: 0.577.0(react@19.2.7) - motion: - specifier: 12.40.0 - version: 12.40.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - next: - specifier: 16.3.8 - version: 16.3.8(@opentelemetry/api@1.9.0)(@types/node@22.19.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - nextra: - specifier: 4.6.1 - version: 4.6.1(next@16.3.8(@opentelemetry/api@1.9.0)(@types/node@22.19.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3) - nextra-theme-docs: - specifier: 4.6.1 - version: 4.6.1(@types/react@19.2.16)(immer@11.1.15)(next@16.3.8(@opentelemetry/api@1.9.0)(@types/node@22.19.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(nextra@4.6.1(next@16.3.8(@opentelemetry/api@1.9.0)(@types/node@22.19.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7)) - posthog-js: - specifier: 1.379.0 - version: 1.379.0 - posthog-node: - specifier: 5.35.12 - version: 5.35.12 - react: - specifier: 19.2.7 - version: 19.2.7 - react-dom: - specifier: 19.2.7 - version: 19.2.7(react@19.2.7) - react-hook-form: - specifier: 7.77.0 - version: 7.77.0(react@19.2.7) - react-instantsearch: - specifier: 7.34.0 - version: 7.34.0(algoliasearch@5.53.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react-markdown: - specifier: 10.1.0 - version: 10.1.0(@types/react@19.2.16)(react@19.2.7) - react-syntax-highlighter: - specifier: 16.1.1 - version: 16.1.1(react@19.2.7) - remark-gfm: - specifier: 4.0.1 - version: 4.0.1 - swagger-ui-react: - specifier: 5.32.14 - version: 5.32.14(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - tailwindcss-animate: - specifier: 1.0.7 - version: 1.0.7(tailwindcss@4.3.0) - unist-util-visit: - specifier: 5.1.0 - version: 5.1.0 - yaml: - specifier: 2.8.3 - version: 2.8.3 - zod: - specifier: 4.3.6 - version: 4.3.6 - devDependencies: - '@anthropic-ai/sdk': - specifier: 0.91.1 - version: 0.91.1(zod@4.3.6) - '@biomejs/biome': - specifier: 2.3.2 - version: 2.3.2 - '@octokit/rest': - specifier: 22.0.1 - version: 22.0.1 - '@tailwindcss/postcss': - specifier: 4.3.0 - version: 4.3.0 - '@tailwindcss/typography': - specifier: 0.5.19 - version: 0.5.19(tailwindcss@4.3.0) - '@types/hast': - specifier: 3.0.5 - version: 3.0.5 - '@types/mdast': - specifier: 4.0.4 - version: 4.0.4 - '@types/mdx': - specifier: 2.0.13 - version: 2.0.13 - '@types/node': - specifier: 22.19.17 - version: 22.19.17 - '@types/react': - specifier: 19.2.16 - version: 19.2.16 - '@types/react-dom': - specifier: 19.2.3 - version: 19.2.3(@types/react@19.2.16) - '@types/react-syntax-highlighter': - specifier: 15.5.13 - version: 15.5.13 - '@types/unist': - specifier: 3.0.3 - version: 3.0.3 - chalk: - specifier: 5.6.2 - version: 5.6.2 - commander: - specifier: 14.0.3 - version: 14.0.3 - dotenv: - specifier: 17.4.2 - version: 17.4.2 - fast-glob: - specifier: 3.3.3 - version: 3.3.3 - husky: - specifier: 9.1.7 - version: 9.1.7 - lint-staged: - specifier: 16.4.0 - version: 16.4.0 - next-validate-link: - specifier: 1.6.6 - version: 1.6.6(@types/mdast@4.0.4)(@types/unist@3.0.3) - openai: - specifier: 6.41.0 - version: 6.41.0(zod@4.3.6) - ora: - specifier: 9.4.0 - version: 9.4.0 - postcss: - specifier: 8.5.26 - version: 8.5.26 - tailwindcss: - specifier: 4.3.0 - version: 4.3.0 - tsx: - specifier: ^4.23.8 - version: 4.23.8 - typescript: - specifier: 5.9.3 - version: 5.9.3 - ultracite: - specifier: 6.1.0 - version: 6.1.0(typescript@5.9.3) - vite: - specifier: 7.3.5 - version: 7.3.5(@types/node@22.19.17)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.23.8)(yaml@2.8.3) - vitest: - specifier: 4.1.8 - version: 4.1.8(@opentelemetry/api@1.9.0)(@types/node@22.19.17)(vite@7.3.5(@types/node@22.19.17)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.23.8)(yaml@2.8.3)) - -packages: - - '@ai-sdk/gateway@4.0.28': - resolution: {integrity: sha512-ee9TsNO3mkgHDWmTmJ8Fvltr6PFh52zhrV2+FaKJY3F3iu7kWZi5tCRJCR1HVhhlpj6lHniUb28nLQdPHkA/1Q==} - engines: {node: '>=22'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/mcp@2.0.16': - resolution: {integrity: sha512-jC2r+StEuk8uIldfP4j8nBwOEBJ2TRwy/bz71SHMZ3NG6PSbvRPXGfyoHHR6JTm1O5GcIww0f6B8LznzkrseDA==} - engines: {node: '>=22'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/provider-utils@5.0.12': - resolution: {integrity: sha512-bbhlOgHeYwrIGheLkM6fhS8hVger8uFPmcOLg+kxc9EFh7y30XYorWhthlYAgpadO3SJhFZrIcEknN7qEqEVvA==} - engines: {node: '>=22'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/provider@4.0.3': - resolution: {integrity: sha512-e0CpNWJUY7OxAFAnCZkw+ri9QOHWwTs1tXP42782KFGCU07qt8NiXCrCVowyCB5dP2r5/Uls+g2oPd8kOJn9dw==} - engines: {node: '>=22'} - - '@ai-sdk/react@4.0.40': - resolution: {integrity: sha512-z3Ht1ERCEYmt7vo6IZYXlPPNEH/SYz8CAdRWuxA+MgA5p8AXFnor/U6WhZmsAmyzhQk1Oe138PtlW7ee4Ap4qg==} - engines: {node: '>=22'} - peerDependencies: - react: ^18 || ~19.0.1 || ~19.1.2 || ^19.2.1 - - '@algolia/abtesting@1.19.0': - resolution: {integrity: sha512-Lhnez3hhXHk25lfxLAMxvkP4fmN3+1RgADhD2ssMDBYuAsDVReeyP+3SGRx+ntq8ijMrLqUyfvO72TB6jsTteQ==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-abtesting@5.53.0': - resolution: {integrity: sha512-0ZjA5Hcmaoz5Lj6OG0zhfIyeqzJZnLW2CRJA1W17UwMFGRtZAJ9yJKRvPEDA6gkpsIoQxORTSW6sWFiuYncPNQ==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-analytics@5.53.0': - resolution: {integrity: sha512-kWNodP75iiEaOtemC9F/hlxNBG5E2QUjN1BusnE6m2b4l7Qh/BUO3fGCVsmKJI65VO4VKGGmT43ICvHtTcJ2JQ==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-common@5.53.0': - resolution: {integrity: sha512-YPN45TXD9Wrse185t/Ta7nktZsqpv97oOjCzp2sblHnCL6rBc9TDeJAg1IGl2UpdwnSD05Zu/5wLB4watOUMyg==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-insights@5.53.0': - resolution: {integrity: sha512-qAcYTDJE6m924FDDUQvdD6vh7DYaqOeSpFS74IP37/JRV0v4cGBauyxTF2WzDnokUylQDbqreoFIJZfg0Fitmw==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-personalization@5.53.0': - resolution: {integrity: sha512-fQaY+DkSJOpuUVUe8MQTwrdiKAqkJGhpDarB08duBn/sUv7Bkib6MDRQauCcWTWTe4HIW+EbwQP9R4kci1V/Yw==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-query-suggestions@5.53.0': - resolution: {integrity: sha512-o72tsiEZGfeS/dxL9IADfzcZWGEwKDEe5CvtrBuT//3JR+SHuTtHRI2ZTf7D7bcKagcbojvO8hnkHdfoakSlYg==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-search@5.53.0': - resolution: {integrity: sha512-Ds16IyPm/dNJPCU8OzApo2gwGrgWT5BYHhE3NFwZbpCveqyvPDB9sZDDkJ5DsdOGT2aC+R3i0/M1OVXF2qdgPg==} - engines: {node: '>= 14.0.0'} - - '@algolia/events@4.0.1': - resolution: {integrity: sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==} - - '@algolia/ingestion@1.53.0': - resolution: {integrity: sha512-oNbT6z4NwD8Pou9VPINGlN/tlG1afESh2EbxqnP6rwl95xKVD/Zlciis1PpNeO/9U/rrajc1+7DcfKi03tX1KQ==} - engines: {node: '>= 14.0.0'} - - '@algolia/monitoring@1.53.0': - resolution: {integrity: sha512-G+KZb/yd+qAOFn/cEvTGeLxQm8aP3a0od50l3z/ylccY+/o4YG3TNcjU1tFQHW4mXC137GPyR7W70R0kRQDLnA==} - engines: {node: '>= 14.0.0'} - - '@algolia/recommend@5.53.0': - resolution: {integrity: sha512-6aVfYd55Un6IUgPLbo84WfgFZlS3L0vA1ttzXL5vahHewUJ8jYgd89TzlWRTeej7w70mb9RWsVlFYGmJ/diQww==} - engines: {node: '>= 14.0.0'} - - '@algolia/requester-browser-xhr@5.53.0': - resolution: {integrity: sha512-ke27DqgzCOlt+RbeEdCxtXxMQOnAOi8ujr2wid0DmDKzR95Kw/f9sBsuhBxtjevCqJRJszfRTLY0B1pbO6IhkA==} - engines: {node: '>= 14.0.0'} - - '@algolia/requester-fetch@5.53.0': - resolution: {integrity: sha512-GngiOqt2Gq4oLno6yXQVj9om+qSO9SWAoduoTOEg79dKZ62brB8OOIvSJG/vDNoanYi6a7Al9uDZwXvi+bcVTg==} - engines: {node: '>= 14.0.0'} - - '@algolia/requester-node-http@5.53.0': - resolution: {integrity: sha512-6mF9LZMUk0QqWvrnxkxBqhswwz6Xfiwy6/gmTzL5HrlhdVG3ITAqGV2k3XmVThP1h0Ulc3VQwiNCD7/Nr4JNlQ==} - engines: {node: '>= 14.0.0'} - - '@alloc/quick-lru@5.2.0': - resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} - engines: {node: '>=10'} - - '@antfu/install-pkg@1.1.0': - resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} - - '@anthropic-ai/sdk@0.91.1': - resolution: {integrity: sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw==} - hasBin: true - peerDependencies: - zod: ^3.25.0 || ^4.0.0 - peerDependenciesMeta: - zod: - optional: true - - '@arcadeai/arcadejs@2.4.1': - resolution: {integrity: sha512-DLpvvDBvRNjzVmFYJ+upPrUJ8YRBoGWqPfzhuFp2+lh5Shxm0l9qLwe1Ah4pKvuCatU7TLOOTkFpICitTuuJeQ==} - hasBin: true - - '@arcadeai/design-system@7.11.0': - resolution: {integrity: sha512-zsrmQf6yryIrqPJXljLxazFSvElj8fdSBZAC24WevD7hMcS9h0tkurAqyQevbHP0iisWCgNPYhlrj6GZIC+G4g==} - engines: {bun: '>=1.3.5'} - peerDependencies: - '@hookform/resolvers': '>=5.2.1' - lucide-react: '>=0.544.0' - react: '>=19.2.1' - react-dom: '>=19.2.1' - react-hook-form: '>=7.62.0' - recharts: '>=3.2.0' - tailwindcss: '>=4.1.13' - - '@arcadeai/ui-kit@0.14.0': - resolution: {integrity: sha512-/VhqP2+mQ8fbjvGiq6pcYISODuKbcXHfhsD4rNO79P/bFEY5A9xupG1BreDWwpwOQm6sNy8fjwLs7sbQz7KL2Q==} - engines: {bun: '>=1.3.5'} - peerDependencies: - '@ai-sdk/react': '>=3.0.0' - '@arcadeai/design-system': '>=7.0.0' - '@tanstack/react-query': '>=5.0.0' - '@tanstack/react-router': '>=1.0.0' - ai: '>=6.0.0' - lucide-react: '>=0.400.0' - react: '>=19.0.0' - react-dom: '>=19.0.0' - tailwindcss: '>=4.0.0' - - '@babel/runtime-corejs3@7.28.4': - resolution: {integrity: sha512-h7iEYiW4HebClDEhtvFObtPmIvrd1SSfpI9EhOeKk4CtIK/ngBWFpuhCzhdmRKtg71ylcue+9I6dv54XYO1epQ==} - engines: {node: '>=6.9.0'} - - '@babel/runtime@7.28.4': - resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} - engines: {node: '>=6.9.0'} - - '@babel/runtime@7.29.7': - resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} - engines: {node: '>=6.9.0'} - - '@base-ui/react@1.6.0': - resolution: {integrity: sha512-/jzjTWJYXhRFO45Bev9lc3cHbmjzCMpUqbMZ2AgKy/z25mY9B6shGSNcXcjQar9n5doM0KYW1W8fcFv2jZBuMw==} - 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.1': - resolution: {integrity: sha512-gFFiltORVmW/N6IILTGxizP3PBpVpysqML1ALY5Vk0mH+7faVkCknOU31goYHN5Aoek2dkjxva1XOD2Ce9WuIg==} - peerDependencies: - '@types/react': ^17 || ^18 || ^19 - react: ^17 || ^18 || ^19 - react-dom: ^17 || ^18 || ^19 - peerDependenciesMeta: - '@types/react': - optional: true - - '@biomejs/biome@2.3.2': - resolution: {integrity: sha512-8e9tzamuDycx7fdrcJ/F/GDZ8SYukc5ud6tDicjjFqURKYFSWMl0H0iXNXZEGmcmNUmABgGuHThPykcM41INgg==} - engines: {node: '>=14.21.3'} - hasBin: true - - '@biomejs/cli-darwin-arm64@2.3.2': - resolution: {integrity: sha512-4LECm4kc3If0JISai4c3KWQzukoUdpxy4fRzlrPcrdMSRFksR9ZoXK7JBcPuLBmd2SoT4/d7CQS33VnZpgBjew==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [darwin] - - '@biomejs/cli-darwin-x64@2.3.2': - resolution: {integrity: sha512-jNMnfwHT4N3wi+ypRfMTjLGnDmKYGzxVr1EYAPBcauRcDnICFXN81wD6wxJcSUrLynoyyYCdfW6vJHS/IAoTDA==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [darwin] - - '@biomejs/cli-linux-arm64-musl@2.3.2': - resolution: {integrity: sha512-2Zz4usDG1GTTPQnliIeNx6eVGGP2ry5vE/v39nT73a3cKN6t5H5XxjcEoZZh62uVZvED7hXXikclvI64vZkYqw==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [linux] - - '@biomejs/cli-linux-arm64@2.3.2': - resolution: {integrity: sha512-amnqvk+gWybbQleRRq8TMe0rIv7GHss8mFJEaGuEZYWg1Tw14YKOkeo8h6pf1c+d3qR+JU4iT9KXnBKGON4klw==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [linux] - - '@biomejs/cli-linux-x64-musl@2.3.2': - resolution: {integrity: sha512-gzB19MpRdTuOuLtPpFBGrV3Lq424gHyq2lFj8wfX9tvLMLdmA/R9C7k/mqBp/spcbWuHeIEKgEs3RviOPcWGBA==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [linux] - - '@biomejs/cli-linux-x64@2.3.2': - resolution: {integrity: sha512-8BG/vRAhFz1pmuyd24FQPhNeueLqPtwvZk6yblABY2gzL2H8fLQAF/Z2OPIc+BPIVPld+8cSiKY/KFh6k81xfA==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [linux] - - '@biomejs/cli-win32-arm64@2.3.2': - resolution: {integrity: sha512-lCruqQlfWjhMlOdyf5pDHOxoNm4WoyY2vZ4YN33/nuZBRstVDuqPPjS0yBkbUlLEte11FbpW+wWSlfnZfSIZvg==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [win32] - - '@biomejs/cli-win32-x64@2.3.2': - resolution: {integrity: sha512-6Ee9P26DTb4D8sN9nXxgbi9Dw5vSOfH98M7UlmkjKB2vtUbrRqCbZiNfryGiwnPIpd6YUoTl7rLVD2/x1CyEHQ==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [win32] - - '@braintree/sanitize-url@7.1.2': - resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==} - - '@chevrotain/types@11.1.2': - resolution: {integrity: sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==} - - '@clack/core@0.5.0': - resolution: {integrity: sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==} - - '@clack/prompts@0.11.0': - resolution: {integrity: sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==} - - '@date-fns/tz@1.4.1': - resolution: {integrity: sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==} - - '@emnapi/runtime@1.11.3': - resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} - - '@esbuild/aix-ppc64@0.28.1': - resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.28.1': - resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.28.1': - resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.28.1': - resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.28.1': - resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.28.1': - resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.28.1': - resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.28.1': - resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.28.1': - resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.28.1': - resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.28.1': - resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.28.1': - resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.28.1': - resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.28.1': - resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.28.1': - resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.28.1': - resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.28.1': - resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.28.1': - resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.28.1': - resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.28.1': - resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.28.1': - resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.28.1': - resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.28.1': - resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.28.1': - resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.28.1': - resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.28.1': - resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@floating-ui/core@1.7.5': - resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} - - '@floating-ui/dom@1.7.6': - resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} - - '@floating-ui/react-dom@2.1.8': - resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - - '@floating-ui/react@0.26.28': - resolution: {integrity: sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - - '@floating-ui/utils@0.2.11': - resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} - - '@fontsource-variable/geist@5.2.9': - resolution: {integrity: sha512-TP+QSBG3wxKGPE33CbMy/L0Nu3qvJ6Fy81Yc4LnQ95xH+i+cfEp8fyU8/kfV14YwszxIFPhnoMTbjL71waVpyQ==} - - '@formatjs/intl-localematcher@0.6.2': - resolution: {integrity: sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==} - - '@headlessui/react@2.2.9': - resolution: {integrity: sha512-Mb+Un58gwBn0/yWZfyrCh0TJyurtT+dETj7YHleylHk5od3dv2XqETPGWMyQ5/7sYN7oWdyM1u9MvC0OC8UmzQ==} - engines: {node: '>=10'} - peerDependencies: - react: ^18 || ^19 || ^19.0.0-rc - react-dom: ^18 || ^19 || ^19.0.0-rc - - '@hookform/resolvers@5.2.2': - resolution: {integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==} - peerDependencies: - react-hook-form: ^7.55.0 - - '@iconify/types@2.0.0': - resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} - - '@iconify/utils@3.1.0': - resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==} - - '@img/colour@1.1.0': - resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} - engines: {node: '>=18'} - - '@img/sharp-darwin-arm64@0.35.5': - resolution: {integrity: sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==} - engines: {node: '>=20.9.0'} - cpu: [arm64] - os: [darwin] - - '@img/sharp-darwin-x64@0.35.5': - resolution: {integrity: sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==} - engines: {node: '>=20.9.0'} - cpu: [x64] - os: [darwin] - - '@img/sharp-freebsd-wasm32@0.35.5': - resolution: {integrity: sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==} - engines: {node: '>=20.9.0'} - os: [freebsd] - - '@img/sharp-libvips-darwin-arm64@1.3.4': - resolution: {integrity: sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==} - cpu: [arm64] - os: [darwin] - - '@img/sharp-libvips-darwin-x64@1.3.4': - resolution: {integrity: sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-linux-arm64@1.3.4': - resolution: {integrity: sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==} - cpu: [arm64] - os: [linux] - - '@img/sharp-libvips-linux-arm@1.3.4': - resolution: {integrity: sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==} - cpu: [arm] - os: [linux] - - '@img/sharp-libvips-linux-ppc64@1.3.4': - resolution: {integrity: sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==} - cpu: [ppc64] - os: [linux] - - '@img/sharp-libvips-linux-riscv64@1.3.4': - resolution: {integrity: sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==} - cpu: [riscv64] - os: [linux] - - '@img/sharp-libvips-linux-s390x@1.3.4': - resolution: {integrity: sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==} - cpu: [s390x] - os: [linux] - - '@img/sharp-libvips-linux-x64@1.3.4': - resolution: {integrity: sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==} - cpu: [x64] - os: [linux] - - '@img/sharp-libvips-linuxmusl-arm64@1.3.4': - resolution: {integrity: sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==} - cpu: [arm64] - os: [linux] - - '@img/sharp-libvips-linuxmusl-x64@1.3.4': - resolution: {integrity: sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==} - cpu: [x64] - os: [linux] - - '@img/sharp-linux-arm64@0.35.5': - resolution: {integrity: sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==} - engines: {node: '>=20.9.0'} - cpu: [arm64] - os: [linux] - - '@img/sharp-linux-arm@0.35.5': - resolution: {integrity: sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==} - engines: {node: '>=20.9.0'} - cpu: [arm] - os: [linux] - - '@img/sharp-linux-ppc64@0.35.5': - resolution: {integrity: sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==} - engines: {node: '>=20.9.0'} - cpu: [ppc64] - os: [linux] - - '@img/sharp-linux-riscv64@0.35.5': - resolution: {integrity: sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==} - engines: {node: '>=20.9.0'} - cpu: [riscv64] - os: [linux] - - '@img/sharp-linux-s390x@0.35.5': - resolution: {integrity: sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==} - engines: {node: '>=20.9.0'} - cpu: [s390x] - os: [linux] - - '@img/sharp-linux-x64@0.35.5': - resolution: {integrity: sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==} - engines: {node: '>=20.9.0'} - cpu: [x64] - os: [linux] - - '@img/sharp-linuxmusl-arm64@0.35.5': - resolution: {integrity: sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==} - engines: {node: '>=20.9.0'} - cpu: [arm64] - os: [linux] - - '@img/sharp-linuxmusl-x64@0.35.5': - resolution: {integrity: sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==} - engines: {node: '>=20.9.0'} - cpu: [x64] - os: [linux] - - '@img/sharp-wasm32@0.35.5': - resolution: {integrity: sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==} - engines: {node: '>=20.9.0'} - - '@img/sharp-webcontainers-wasm32@0.35.5': - resolution: {integrity: sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==} - engines: {node: '>=20.9.0'} - cpu: [wasm32] - - '@img/sharp-win32-arm64@0.35.5': - resolution: {integrity: sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==} - engines: {node: '>=20.9.0'} - cpu: [arm64] - os: [win32] - - '@img/sharp-win32-ia32@0.35.5': - resolution: {integrity: sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==} - engines: {node: ^20.9.0} - cpu: [ia32] - os: [win32] - - '@img/sharp-win32-x64@0.35.5': - resolution: {integrity: sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==} - engines: {node: '>=20.9.0'} - cpu: [x64] - os: [win32] - - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - - '@jridgewell/remapping@2.3.5': - resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - - '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - - '@mdx-js/mdx@3.1.1': - resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} - - '@mermaid-js/parser@1.2.1': - resolution: {integrity: sha512-n12NohV3mrUyUL2o93IgG/ifeW9FTyeJn3zDxkhwa8MJ9Fxg3HQMlA3RiGmD/3UnJvheztkjjQAjA2T4LmUcpw==} - - '@napi-rs/simple-git-android-arm-eabi@0.1.22': - resolution: {integrity: sha512-JQZdnDNm8o43A5GOzwN/0Tz3CDBQtBUNqzVwEopm32uayjdjxev1Csp1JeaqF3v9djLDIvsSE39ecsN2LhCKKQ==} - engines: {node: '>= 10'} - cpu: [arm] - os: [android] - - '@napi-rs/simple-git-android-arm64@0.1.22': - resolution: {integrity: sha512-46OZ0SkhnvM+fapWjzg/eqbJvClxynUpWYyYBn4jAj7GQs1/Yyc8431spzDmkA8mL0M7Xo8SmbkzTDE7WwYAfg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [android] - - '@napi-rs/simple-git-darwin-arm64@0.1.22': - resolution: {integrity: sha512-zH3h0C8Mkn9//MajPI6kHnttywjsBmZ37fhLX/Fiw5XKu84eHA6dRyVtMzoZxj6s+bjNTgaMgMUucxPn9ktxTQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - - '@napi-rs/simple-git-darwin-x64@0.1.22': - resolution: {integrity: sha512-GZN7lRAkGKB6PJxWsoyeYJhh85oOOjVNyl+/uipNX8bR+mFDCqRsCE3rRCFGV9WrZUHXkcuRL2laIRn7lLi3ag==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - - '@napi-rs/simple-git-freebsd-x64@0.1.22': - resolution: {integrity: sha512-xyqX1C5I0WBrUgZONxHjZH5a4LqQ9oki3SKFAVpercVYAcx3pq6BkZy1YUOP4qx78WxU1CCNfHBN7V+XO7D99A==} - engines: {node: '>= 10'} - cpu: [x64] - os: [freebsd] - - '@napi-rs/simple-git-linux-arm-gnueabihf@0.1.22': - resolution: {integrity: sha512-4LOtbp9ll93B9fxRvXiUJd1/RM3uafMJE7dGBZGKWBMGM76+BAcCEUv2BY85EfsU/IgopXI6n09TycRfPWOjxA==} - engines: {node: '>= 10'} - cpu: [arm] - os: [linux] - - '@napi-rs/simple-git-linux-arm64-gnu@0.1.22': - resolution: {integrity: sha512-GVOjP/JjCzbQ0kSqao7ctC/1sodVtv5VF57rW9BFpo2y6tEYPCqHnkQkTpieuwMNe+TVOhBUC1+wH0d9/knIHg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - - '@napi-rs/simple-git-linux-arm64-musl@0.1.22': - resolution: {integrity: sha512-MOs7fPyJiU/wqOpKzAOmOpxJ/TZfP4JwmvPad/cXTOWYwwyppMlXFRms3i98EU3HOazI/wMU2Ksfda3+TBluWA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - - '@napi-rs/simple-git-linux-ppc64-gnu@0.1.22': - resolution: {integrity: sha512-L59dR30VBShRUIZ5/cQHU25upNgKS0AMQ7537J6LCIUEFwwXrKORZKJ8ceR+s3Sr/4jempWVvMdjEpFDE4HYww==} - engines: {node: '>= 10'} - cpu: [ppc64] - os: [linux] - - '@napi-rs/simple-git-linux-s390x-gnu@0.1.22': - resolution: {integrity: sha512-4FHkPlCSIZUGC6HiADffbe6NVoTBMd65pIwcd40IDbtFKOgFMBA+pWRqKiQ21FERGH16Zed7XHJJoY3jpOqtmQ==} - engines: {node: '>= 10'} - cpu: [s390x] - os: [linux] - - '@napi-rs/simple-git-linux-x64-gnu@0.1.22': - resolution: {integrity: sha512-Ei1tM5Ho/dwknF3pOzqkNW9Iv8oFzRxE8uOhrITcdlpxRxVrBVptUF6/0WPdvd7R9747D/q61QG/AVyWsWLFKw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - - '@napi-rs/simple-git-linux-x64-musl@0.1.22': - resolution: {integrity: sha512-zRYxg7it0p3rLyEJYoCoL2PQJNgArVLyNavHW03TFUAYkYi5bxQ/UFNVpgxMaXohr5yu7qCBqeo9j4DWeysalg==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - - '@napi-rs/simple-git-win32-arm64-msvc@0.1.22': - resolution: {integrity: sha512-XGFR1fj+Y9cWACcovV2Ey/R2xQOZKs8t+7KHPerYdJ4PtjVzGznI4c2EBHXtdOIYvkw7tL5rZ7FN1HJKdD5Quw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - - '@napi-rs/simple-git-win32-ia32-msvc@0.1.22': - resolution: {integrity: sha512-Gqr9Y0gs6hcNBA1IXBpoqTFnnIoHuZGhrYqaZzEvGMLrTrpbXrXVEtX3DAAD2RLc1b87CPcJ49a7sre3PU3Rfw==} - engines: {node: '>= 10'} - cpu: [ia32] - os: [win32] - - '@napi-rs/simple-git-win32-x64-msvc@0.1.22': - resolution: {integrity: sha512-hQjcreHmUcpw4UrtkOron1/TQObfe484lxiXFLLUj7aWnnnOVs1mnXq5/Bo9+3NYZldFpFRJPdPBeHCisXkKJg==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - - '@napi-rs/simple-git@0.1.22': - resolution: {integrity: sha512-bMVoAKhpjTOPHkW/lprDPwv5aD4R4C3Irt8vn+SKA9wudLe9COLxOhurrKRsxmZccUbWXRF7vukNeGUAj5P8kA==} - engines: {node: '>= 10'} - - '@next/env@16.3.8': - resolution: {integrity: sha512-Al9zqHVV7TJv0eFuOU4U7Lvv74PTih4Ch63sk2xCIpSTkE3udFnaOcnzP2lQVymiL7yS9Cj2iClUXlR3EQ5sEw==} - - '@next/swc-darwin-arm64@16.3.8': - resolution: {integrity: sha512-2JPRMh2nmQG5CiL7cXGL9AGwnPWJQ//cTtAUCT+w511QHk79SYz3LGv/pc5X643B/WEO0rvu3Yww0hqwt3kgeA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - - '@next/swc-darwin-x64@16.3.8': - resolution: {integrity: sha512-GZtCCOBKJ4leVIT/Th0llWKhD1ca92lzbQiS5R5ON9QkoiFnilFsebDae1JU2a3HWoKMEmEZWGs1AGLavVM72Q==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - - '@next/swc-linux-arm64-gnu@16.3.8': - resolution: {integrity: sha512-O659ygeQYqneJ1fBKMpFxIFqYkYswu8IAS1OCKK/4f3ZgJJm1dRz4fVJZRi/kLLWjnBKnebOePA4WNv+sV1pVA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - - '@next/swc-linux-arm64-musl@16.3.8': - resolution: {integrity: sha512-dSjKSyWpzxoO1d3DIZZcP4XJcNaKeLmxQMFOiYl5vuBRMmweIqnAhty8tAmRsvTss779cK1FtYnDMj40e4TQlg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - - '@next/swc-linux-x64-gnu@16.3.8': - resolution: {integrity: sha512-lbqOuz3RPRcv+o9msNsJw5x4+Y1ZwPTs6vmL6DCf7i0fZfvng/F59wyeDwqHIvV0mK//RBy/jJkZ+nCKsSMXjQ==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - - '@next/swc-linux-x64-musl@16.3.8': - resolution: {integrity: sha512-+316WswI8ScVgZeUd+1KGaXkHhaYQzCjvH/05TZSpJ8zBizb1a4G7DtO7F12jcBIqMOtsz9ji1t48fmKtzqsGA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - - '@next/swc-win32-arm64-msvc@16.3.8': - resolution: {integrity: sha512-ji0gd4kMYUxO+1fJBIbiBVRCjzG/lloiyCccnlebvb1ZJ5qXCPZqYg4Jl1DrrixnWNMKylzgpmMWx0yNDYXlzw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - - '@next/swc-win32-x64-msvc@16.3.8': - resolution: {integrity: sha512-WcTlaKt/TWkh5kUjdJcUmB1XgZ+1c6fz4Y9fDHL73YNSdGaUWjceeWrrlwF0nv19iABYWC4iAq1oX1w4Bn0vfg==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - - '@next/third-parties@16.3.8': - resolution: {integrity: sha512-6Ee+OFZczvnG0FVsHaEjQeMRy1QQ+TNHSz7VxcBh87WQqrp/ahaYvSekmlBPtRuh6AD10fRmz1Pws5UcWrokxA==} - peerDependencies: - next: ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0-beta.0 - react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - - '@octokit/auth-token@6.0.0': - resolution: {integrity: sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==} - engines: {node: '>= 20'} - - '@octokit/core@7.0.6': - resolution: {integrity: sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==} - engines: {node: '>= 20'} - - '@octokit/endpoint@11.0.2': - resolution: {integrity: sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ==} - engines: {node: '>= 20'} - - '@octokit/graphql@9.0.3': - resolution: {integrity: sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==} - engines: {node: '>= 20'} - - '@octokit/openapi-types@27.0.0': - resolution: {integrity: sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==} - - '@octokit/plugin-paginate-rest@14.0.0': - resolution: {integrity: sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==} - engines: {node: '>= 20'} - peerDependencies: - '@octokit/core': '>=6' - - '@octokit/plugin-request-log@6.0.0': - resolution: {integrity: sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==} - engines: {node: '>= 20'} - peerDependencies: - '@octokit/core': '>=6' - - '@octokit/plugin-rest-endpoint-methods@17.0.0': - resolution: {integrity: sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==} - engines: {node: '>= 20'} - peerDependencies: - '@octokit/core': '>=6' - - '@octokit/request-error@7.1.0': - resolution: {integrity: sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==} - engines: {node: '>= 20'} - - '@octokit/request@10.0.7': - resolution: {integrity: sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA==} - engines: {node: '>= 20'} - - '@octokit/rest@22.0.1': - resolution: {integrity: sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw==} - engines: {node: '>= 20'} - - '@octokit/types@16.0.0': - resolution: {integrity: sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==} - - '@opentelemetry/api-logs@0.208.0': - resolution: {integrity: sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==} - engines: {node: '>=8.0.0'} - - '@opentelemetry/api@1.9.0': - resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} - engines: {node: '>=8.0.0'} - - '@opentelemetry/core@2.10.0': - resolution: {integrity: sha512-/wNZ8twnEQQA4HoHu22+vcsdru6pWPWxW+7w+FlxT6Id7PE/WIbZmVKkte+PF72e0F2dnImFeHD2syyE1Mw6MQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - - '@opentelemetry/exporter-logs-otlp-http@0.208.0': - resolution: {integrity: sha512-jOv40Bs9jy9bZVLo/i8FwUiuCvbjWDI+ZW13wimJm4LjnlwJxGgB+N/VWOZUTpM+ah/awXeQqKdNlpLf2EjvYg==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/otlp-exporter-base@0.208.0': - resolution: {integrity: sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/otlp-transformer@0.208.0': - resolution: {integrity: sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/resources@2.2.0': - resolution: {integrity: sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.10.0' - - '@opentelemetry/resources@2.5.0': - resolution: {integrity: sha512-F8W52ApePshpoSrfsSk1H2yJn9aKjCrbpQF1M9Qii0GHzbfVeFUB+rc3X4aggyZD8x9Gu3Slua+s6krmq6Dt8g==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.10.0' - - '@opentelemetry/sdk-logs@0.208.0': - resolution: {integrity: sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.4.0 <1.10.0' - - '@opentelemetry/sdk-metrics@2.2.0': - resolution: {integrity: sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.9.0 <1.10.0' - - '@opentelemetry/sdk-trace-base@2.2.0': - resolution: {integrity: sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.10.0' - - '@opentelemetry/semantic-conventions@1.39.0': - resolution: {integrity: sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==} - engines: {node: '>=14'} - - '@ory/client@1.22.37': - resolution: {integrity: sha512-UEYHSveRRD3TCT0JB54ZKQoRMEHn7HTsNsMOjz2t3bzCs5aNjgpjYIBK4z5zE2PwNrcInZyQXfRiwK+LIUaOug==} - - '@posthog/core@1.30.3': - resolution: {integrity: sha512-mGIN4Du1a8fKxV5WfnEtogq3VOQLa4PfWMUg5uMIMDD+fp5bhTA4QG4oYAea4vBTZ1ZOanQbjXcX4b5k2X93Qw==} - - '@posthog/types@1.379.0': - resolution: {integrity: sha512-QJLzyV22iZKy5eEXKn9IB1swSVo2JigIawebBha+BmsePsY3XA0HAcq2B+D8Km5BHnbE+SGm5aHxFG3DlVE1yg==} - - '@protobufjs/aspromise@1.1.2': - resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} - - '@protobufjs/base64@1.1.2': - resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} - - '@protobufjs/codegen@2.0.5': - resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} - - '@protobufjs/eventemitter@1.1.1': - resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==} - - '@protobufjs/fetch@1.1.1': - resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==} - - '@protobufjs/float@1.0.2': - resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} - - '@protobufjs/path@1.1.2': - resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} - - '@protobufjs/pool@1.1.0': - resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} - - '@protobufjs/utf8@1.1.2': - resolution: {integrity: sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==} - - '@radix-ui/primitive@1.1.3': - resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} - - '@radix-ui/react-compose-refs@1.1.2': - resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-context@1.1.2': - resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-dialog@1.1.15': - resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-dismissable-layer@1.1.11': - resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-focus-guards@1.1.3': - resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-focus-scope@1.1.7': - resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-id@1.1.1': - resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-portal@1.1.9': - resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-presence@1.1.5': - resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-primitive@2.1.3': - resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-primitive@2.1.4': - resolution: {integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-slot@1.2.3': - resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-slot@1.2.4': - resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-callback-ref@1.1.1': - resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-controllable-state@1.2.2': - resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-effect-event@0.0.2': - resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-escape-keydown@1.1.1': - resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-layout-effect@1.1.1': - resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@react-aria/focus@3.21.2': - resolution: {integrity: sha512-JWaCR7wJVggj+ldmM/cb/DXFg47CXR55lznJhZBh4XVqJjMKwaOOqpT5vNN7kpC1wUpXicGNuDnJDN1S/+6dhQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/interactions@3.25.6': - resolution: {integrity: sha512-5UgwZmohpixwNMVkMvn9K1ceJe6TzlRlAfuYoQDUuOkk62/JVJNDLAPKIf5YMRc7d2B0rmfgaZLMtbREb0Zvkw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/ssr@3.9.10': - resolution: {integrity: sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==} - engines: {node: '>= 12'} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/utils@3.31.0': - resolution: {integrity: sha512-ABOzCsZrWzf78ysswmguJbx3McQUja7yeGj6/vZo4JVsZNlxAN+E9rs381ExBRI0KzVo6iBTeX5De8eMZPJXig==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-stately/flags@3.1.2': - resolution: {integrity: sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==} - - '@react-stately/utils@3.10.8': - resolution: {integrity: sha512-SN3/h7SzRsusVQjQ4v10LaVsDc81jyyR0DD5HnsQitm/I5WDpaSr2nRHtyloPFU48jlql1XX/S04T2DLQM7Y3g==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-types/shared@3.32.1': - resolution: {integrity: sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@reduxjs/toolkit@2.12.0': - resolution: {integrity: sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw==} - peerDependencies: - react: ^16.9.0 || ^17.0.0 || ^18 || ^19 - react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 - peerDependenciesMeta: - react: - optional: true - react-redux: - optional: true - - '@rollup/rollup-android-arm-eabi@4.62.2': - resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.62.2': - resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.62.2': - resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.62.2': - resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.62.2': - resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.62.2': - resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.62.2': - resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm-musleabihf@4.62.2': - resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm64-gnu@4.62.2': - resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-arm64-musl@4.62.2': - resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-loong64-gnu@4.62.2': - resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} - cpu: [loong64] - os: [linux] - - '@rollup/rollup-linux-loong64-musl@4.62.2': - resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} - cpu: [loong64] - os: [linux] - - '@rollup/rollup-linux-ppc64-gnu@4.62.2': - resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} - cpu: [ppc64] - os: [linux] - - '@rollup/rollup-linux-ppc64-musl@4.62.2': - resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} - cpu: [ppc64] - os: [linux] - - '@rollup/rollup-linux-riscv64-gnu@4.62.2': - resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-riscv64-musl@4.62.2': - resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-s390x-gnu@4.62.2': - resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} - cpu: [s390x] - os: [linux] - - '@rollup/rollup-linux-x64-gnu@4.62.2': - resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-linux-x64-musl@4.62.2': - resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-openbsd-x64@4.62.2': - resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==} - cpu: [x64] - os: [openbsd] - - '@rollup/rollup-openharmony-arm64@4.62.2': - resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==} - cpu: [arm64] - os: [openharmony] - - '@rollup/rollup-win32-arm64-msvc@4.62.2': - resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.62.2': - resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.62.2': - resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.62.2': - resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==} - cpu: [x64] - os: [win32] - - '@scarf/scarf@1.4.0': - resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} - - '@shikijs/core@3.20.0': - resolution: {integrity: sha512-f2ED7HYV4JEk827mtMDwe/yQ25pRiXZmtHjWF8uzZKuKiEsJR7Ce1nuQ+HhV9FzDcbIo4ObBCD9GPTzNuy9S1g==} - - '@shikijs/core@4.1.0': - resolution: {integrity: sha512-jLJtSJeuFffqX6/inRE1zqU5aFv2hrszvYgq3OjbAgFRZiWv7abKMDdQzYxuSDfmUPQozZvI/kuy6VMTvnvqTQ==} - engines: {node: '>=20'} - - '@shikijs/engine-javascript@3.20.0': - resolution: {integrity: sha512-OFx8fHAZuk7I42Z9YAdZ95To6jDePQ9Rnfbw9uSRTSbBhYBp1kEOKv/3jOimcj3VRUKusDYM6DswLauwfhboLg==} - - '@shikijs/engine-javascript@4.1.0': - resolution: {integrity: sha512-YquhawCUgaBfhsS72e2Y/dI59gCBNPHu3fEO/tvLaXrTssxZrY5ddjtNLTwndrMgPo8b3IscE+xoICDzpTmlFQ==} - engines: {node: '>=20'} - - '@shikijs/engine-oniguruma@3.20.0': - resolution: {integrity: sha512-Yx3gy7xLzM0ZOjqoxciHjA7dAt5tyzJE3L4uQoM83agahy+PlW244XJSrmJRSBvGYELDhYXPacD4R/cauV5bzQ==} - - '@shikijs/engine-oniguruma@4.1.0': - resolution: {integrity: sha512-axLpjVs45YBvvINa+dJF+NPW+KtFkNXsFr4SDw2BMj9GdeMnGxVB9PQb2xXlJYovslt/nz6giedAyOANkfc7hg==} - engines: {node: '>=20'} - - '@shikijs/langs@3.20.0': - resolution: {integrity: sha512-le+bssCxcSHrygCWuOrYJHvjus6zhQ2K7q/0mgjiffRbkhM4o1EWu2m+29l0yEsHDbWaWPNnDUTRVVBvBBeKaA==} - - '@shikijs/langs@4.1.0': - resolution: {integrity: sha512-nwOMruEkbgdZfQ/b8CgpNBVOpvG1k0N5tbmgiFeqsan401+x3ILqlzZJowSla4Agmq4hG2Uf2wh5jLTEhR8VSg==} - engines: {node: '>=20'} - - '@shikijs/primitive@4.1.0': - resolution: {integrity: sha512-zx2/2Uwj2q9X3KSyYREEhXO23xBw5WUhP4orK2lE4r+t9JGITmEe0JH+wPmJhqHpOT2bRRs6lAL945+LDvOAGw==} - engines: {node: '>=20'} - - '@shikijs/themes@3.20.0': - resolution: {integrity: sha512-U1NSU7Sl26Q7ErRvJUouArxfM2euWqq1xaSrbqMu2iqa+tSp0D1Yah8216sDYbdDHw4C8b75UpE65eWorm2erQ==} - - '@shikijs/themes@4.1.0': - resolution: {integrity: sha512-emCcTnUM7yO2wltYbaxm+yLvcCI4+h8XBKc4KmJ7EZUXoSGjcCHifkI//R4OFit9ewpg7H2/9tjOuXrT2v/Knw==} - engines: {node: '>=20'} - - '@shikijs/twoslash@3.20.0': - resolution: {integrity: sha512-fZz6vB9a0M8iuVF/ydIV4ToC09sbOh/TqxXZFWAh5J8bLiPsyQGtygKMDQ9L0Sdop3co0TIC/JsrLmsbmZwwsw==} - peerDependencies: - typescript: '>=5.5.0' - - '@shikijs/types@3.20.0': - resolution: {integrity: sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw==} - - '@shikijs/types@4.1.0': - resolution: {integrity: sha512-3EQWX54fMpniOrDblzAhiwiJwpiTMW6+B9DWyUd9ska483tbayFYuw47UxwuPknI31bKnySfVQ/QW+jFL4rFdA==} - 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==} - - '@standard-schema/utils@0.3.0': - resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} - - '@streamdown/code@1.1.1': - resolution: {integrity: sha512-i7HTNuDgZWb+VdrNVOam9gQhIc5MSSDXKWXgbUrn/4vSRaSMM+Rtl10MQj4wLWPNpF7p80waJsAqFP8HZfb0Jg==} - peerDependencies: - react: ^18.0.0 || ^19.0.0 - - '@swagger-api/apidom-ast@1.12.0': - resolution: {integrity: sha512-C4Px0M/hNIBf2jFphrHlKe8rN5OuUaxNb3FNandzM8++Uyp9TKjdt0+FI6+sX4s8u3axvQ4c4jrjb0LW7r2jPA==} - - '@swagger-api/apidom-core@1.12.0': - resolution: {integrity: sha512-HXiJ6c7R/Sfpj1nz8Tgzy6jvaKE333b3FA4XTuKrwne/fgGq+ITksp6SaDMp7F/FbvP8iik2OFT9wJ3BRw1wjw==} - - '@swagger-api/apidom-error@1.12.0': - resolution: {integrity: sha512-mdQbyeolFyhyJiVchAJlfOUYntD4p10ORtpVRTLw0vFfqQZtDfr+uMVgiB/kmmyTZ27PoJPR51RDqaXB88aq2Q==} - - '@swagger-api/apidom-json-pointer@1.12.0': - resolution: {integrity: sha512-L6JaaeNjG6J5Ros4MLd+Yb2ZS0RhfJ5fj8w5UtCyf1I0cnwJo7hrRExE5RWXVBtF5nl8mO3eIGUe959hC1DtVQ==} - - '@swagger-api/apidom-ns-a2a-1@1.12.0': - resolution: {integrity: sha512-kYfltECH/D3+bT+7IxYtF7VGNrSeDqqDttiIjx7R3pBgPPbxNWAi/EA4Rx+c7qY56sDz0CdbtZNTH28bNi00og==} - - '@swagger-api/apidom-ns-api-design-systems@1.12.0': - resolution: {integrity: sha512-opXxaAZGxy/IDdUxqfTugtoGeKarb+pDchdVMOWNUD4NZH5weE519dcqCdLMqWtcHX2RL2m9UdcJjGLnAt+GQQ==} - - '@swagger-api/apidom-ns-arazzo-1@1.12.0': - resolution: {integrity: sha512-xqtr7pZ5IQBsHZA6yWbSprVSTzpIbM5FtX40rY7lx4wsm4TyhIfJBxOf99+jFmELt7QHoKCUZ8zZQOdf+uOqLw==} - - '@swagger-api/apidom-ns-asyncapi-2@1.12.0': - resolution: {integrity: sha512-xe3Y3PeRqohaOinEtKTqnUxPoMAopTYAPEnXew5qRcnH7wVWO99J3+XrbY+x9JEXhcAKHpKSWmuNx5o7oU0WdA==} - - '@swagger-api/apidom-ns-asyncapi-3@1.12.0': - resolution: {integrity: sha512-WZ/j/Hk8nsX0lkHiTUFfESkQgGGEba9dwiKMP9OvX9qLsoxJ04Dg1Z7E58WCrmwistEpynMHO+KY3qo2trWOUQ==} - - '@swagger-api/apidom-ns-json-schema-2019-09@1.12.0': - resolution: {integrity: sha512-fWRjOvOiOFqKp0vJgFJn1fm9i+H5eSOQ1J0xWldTnHJPHsYPQkYPLAQdpXcBbBiB5o0JYVeJe7PyWv3DvNxqSA==} - - '@swagger-api/apidom-ns-json-schema-2020-12@1.12.0': - resolution: {integrity: sha512-U0sv+7aD6njyxcvQOS9Lkpr4feJRowMn0/shiIlXrXY1ox3BDJBgipsgx/0IqfQNseP3Zj43Ec5kGk4PFmsjCg==} - - '@swagger-api/apidom-ns-json-schema-draft-4@1.12.0': - resolution: {integrity: sha512-c+NYp+DZvoZ0+3hFgKhQn507kba0HUv8C60cJEqQPCpIKqKpWvpZ6YF8GsKVhJoML4PAr6Zl6SWjFVvOnDUbBg==} - - '@swagger-api/apidom-ns-json-schema-draft-6@1.12.0': - resolution: {integrity: sha512-O8aM7l/+K2ej3AH6f7NbEVADAuZhulLWmAGFFwRBHvE022mUeOQiDrcA1cd5q/s8fY+UFE+GIrfVAq0aBPLvxA==} - - '@swagger-api/apidom-ns-json-schema-draft-7@1.12.0': - resolution: {integrity: sha512-gWTTBmz6CyBytc4v7tMcm0KJ/0xp8n0zyWE1VSBXvLjFylOfvlkzmPL/MD8sqiEqUCGXXSQAyUmyL7heU8uGzQ==} - - '@swagger-api/apidom-ns-openapi-2@1.12.0': - resolution: {integrity: sha512-eISSf1rj7/HZAzOBGdZneUNp4M3FPwvqmKFL9ODBN+kpKFQ438TnYPdMVm60EoiinpvKa7hrKVKj/RubDfvcIQ==} - - '@swagger-api/apidom-ns-openapi-3-0@1.12.0': - resolution: {integrity: sha512-8DqRCkRTxQPtFjRjWiFo5F/IZcBqFFcqjmtoVWbxkUNGyJpgfZjx5Mne/QCB9ZV5vqNeBPF2IYfjQZEvgOBKvA==} - - '@swagger-api/apidom-ns-openapi-3-1@1.12.0': - resolution: {integrity: sha512-6h4NJyCb/I9GkPYCma7SiEp3MlMNH9DFDntamb49AJDBNlibOG1VyR+y3R4+vzUlYOZayMDwe7Z1zC+qN0ulYw==} - - '@swagger-api/apidom-ns-openapi-3-2@1.12.0': - resolution: {integrity: sha512-kkrXfvRPpiJTcK6qr0dB5Jf80kD3jgTo1DU9W6Q5/n06WcyGQhtKTofY7YRHQ52r2Nfpuh9JbxtoD0PIHE0STQ==} - - '@swagger-api/apidom-parser-adapter-a2a-json-1@1.12.0': - resolution: {integrity: sha512-RwUkRb92938NiTriueninR0MaeXYJRZKcZtYyEB5h2JRQ7nQGsCO6a6F1rNwehzLoPeRWLGMYr0wRWP1tTy7fw==} - - '@swagger-api/apidom-parser-adapter-a2a-yaml-1@1.12.0': - resolution: {integrity: sha512-G2L1mRcuAJQICU18cjYH6FIZshYVJOCAmz5JSNknriIL0LYXr8xlbQ7kStTC5cmM9YfxDfD3eLb92vQ+yvpbCA==} - - '@swagger-api/apidom-parser-adapter-api-design-systems-json@1.12.0': - resolution: {integrity: sha512-Co9BcoTlEAcrDIVTE/o0B6+HAlIuwB5hvKMs+w9+P+r6cSDRqx4NOi16iqBH6leCEtO6xU8H152ua0e8hKkQSQ==} - - '@swagger-api/apidom-parser-adapter-api-design-systems-yaml@1.12.0': - resolution: {integrity: sha512-vBnmuMflgcxPkqQX8rXFIWU6GdC0OPMO73LU4iOOjG2kxycET6NTs0zu6mTKRwfCk7PmVR+MzaqLyORCkL3Rjw==} - - '@swagger-api/apidom-parser-adapter-arazzo-json-1@1.12.0': - resolution: {integrity: sha512-5SHEWHdLXpbugsDN2NVYxqh87Sson4pDS+Yk9Zp00cJSl2dNtLZ+LHq3dtIgjFcKy9yOCnaiKjzqoZtpdQB1zQ==} - - '@swagger-api/apidom-parser-adapter-arazzo-yaml-1@1.12.0': - resolution: {integrity: sha512-l0ocWS0IcWYx0ZFosEQguOS72dJXYnEC42KrQgYJMvZCfpMZPtP4HahAYryFxRZWiHZnhunkSChv8MRnBplcQA==} - - '@swagger-api/apidom-parser-adapter-asyncapi-json-2@1.12.0': - resolution: {integrity: sha512-oZt3H7qH4ttU+RILLQ8G8tgOLe+hRMQDh7q/W/Dfxc2xGGsLb6jYG4gl45ry/QHDpudCc2SKC3GxdXfQ3Dl4pg==} - - '@swagger-api/apidom-parser-adapter-asyncapi-json-3@1.12.0': - resolution: {integrity: sha512-O4osrUo/q0ErGPqmCHfYEdRpeH894Dix0AQwLwuovgIS5OxWMa2r6Z8mzz3emRoyVi9ucF+0//OFm7WeU7NcGg==} - - '@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@1.12.0': - resolution: {integrity: sha512-AfcdwWREQhBAEbVTwIVjFLvW/j5wTXmY8/VkKp1OmF7zXyyZDMA+xRAoGZlOlc+KNJ46O6yeN9CLfUaJ0IoRog==} - - '@swagger-api/apidom-parser-adapter-asyncapi-yaml-3@1.12.0': - resolution: {integrity: sha512-+gAKV6dwr1neG3ngONPYWYRATrYD/ooRO+dY6kOgc2yy99YagtSZjtNNSosvoMdPL8vhI6OkZRyew41kyW+Vwg==} - - '@swagger-api/apidom-parser-adapter-json@1.12.0': - resolution: {integrity: sha512-Tftr63g71VwTsWtafYgI7xrmkp6lcs8p6hwo06UzZICTyEsfMrye6Hjvs2TM0KBO/e6Hw8cTdCIBVtEV10Knqw==} - - '@swagger-api/apidom-parser-adapter-openapi-json-2@1.12.0': - resolution: {integrity: sha512-2cMgq3NBAS7magAFqgtBZOoi3vRR7/txQOOA1nzEODiS4WV4ERbe2B59+uaQ7+yzJlkm0OPqzFhXy3IkOB6Z0g==} - - '@swagger-api/apidom-parser-adapter-openapi-json-3-0@1.12.0': - resolution: {integrity: sha512-ws5vsTv8KtrwzPPGwyeNUrEGYLlsRSByTWHFXH8hU8eGbSAcmmKsENhNeEjslimzD2Cev5vJzmdat5oWpKY4fA==} - - '@swagger-api/apidom-parser-adapter-openapi-json-3-1@1.12.0': - resolution: {integrity: sha512-C2/oMuyGarGAPQj5Taob9MOI9Hme6D5Qm/ZyJkRgXBg50ZA3WeaFCr51St6dNU1lDhe8i++cZeWMsp4wegPllw==} - - '@swagger-api/apidom-parser-adapter-openapi-json-3-2@1.12.0': - resolution: {integrity: sha512-kuSIq/Tht5LQ2lezsz6O1x+PM1U5DWeZBQKqHkTgQokhsbxM9dmO6xmSW/aQUk+0jUWf8n6ROrGmscrw6AawWg==} - - '@swagger-api/apidom-parser-adapter-openapi-yaml-2@1.12.0': - resolution: {integrity: sha512-QR0XdBWzjN/8ZmgBrbswP+Bpj+BllUqFiJu43VZNm1M8u/AtbJnsdlTw3coXLVpVpV7ruAKjp0GtsqQyOKNA1w==} - - '@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@1.12.0': - resolution: {integrity: sha512-2Mzft9tXXodoRBqEEXQ1p5MRLrzIDt7SCqZyXF9cBF5ukIdHfgq7vDoX7XUECELzhCf3mMRxLaxH+JcPC26izg==} - - '@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@1.12.0': - resolution: {integrity: sha512-dYrUHnD7r6uhsh2H+gMwxFTSaqSADFHmpG1tFi7b7m/aFbNFCppB63iJ7LYg9Ew6IBej9WrYkr2jXDLKBgsMaA==} - - '@swagger-api/apidom-parser-adapter-openapi-yaml-3-2@1.12.0': - resolution: {integrity: sha512-KrNSQmsmp6iO2o+AxJ9jpwKd4lVmPIaDcWrbp4se5b1MnHUmtf7d7h9kvI5pXhGFGuyA59HMp6qQzNfuS5SMEA==} - - '@swagger-api/apidom-parser-adapter-yaml-1-2@1.12.0': - resolution: {integrity: sha512-vydWYSj1I0t4fsBeu7nAieivBKsW2A7KW9keS+osXLuyYWs1GLHdby5AXRHPv6ktR507vAq8RxQUXkODRxyRCg==} - - '@swagger-api/apidom-reference@1.12.0': - resolution: {integrity: sha512-GApJUvwLROSyM2KfbdvuV/zC6ccb54khvhy2eJ7jPhAk5FU0zZE5Ijt0NmY5nSf97sET87MjrWgCCMszdqPXfw==} - - '@swaggerexpert/cookie@2.0.2': - resolution: {integrity: sha512-DPI8YJ0Vznk4CT+ekn3rcFNq1uQwvUHZhH6WvTSPD0YKBIlMS9ur2RYKghXuxxOiqOam/i4lHJH4xTIiTgs3Mg==} - engines: {node: '>=12.20.0'} - - '@swaggerexpert/json-pointer@2.10.2': - resolution: {integrity: sha512-qMx1nOrzoB+PF+pzb26Q4Tc2sOlrx9Ba2UBNX9hB31Omrq+QoZ2Gly0KLrQWw4Of1AQ4J9lnD+XOdwOdcdXqqw==} - engines: {node: '>=12.20.0'} - - '@swc/helpers@0.5.18': - resolution: {integrity: sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==} - - '@swc/helpers@0.5.23': - resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} - - '@tailwindcss/node@4.3.0': - resolution: {integrity: sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==} - - '@tailwindcss/oxide-android-arm64@4.3.0': - resolution: {integrity: sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [android] - - '@tailwindcss/oxide-darwin-arm64@4.3.0': - resolution: {integrity: sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [darwin] - - '@tailwindcss/oxide-darwin-x64@4.3.0': - resolution: {integrity: sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==} - engines: {node: '>= 20'} - cpu: [x64] - os: [darwin] - - '@tailwindcss/oxide-freebsd-x64@4.3.0': - resolution: {integrity: sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==} - engines: {node: '>= 20'} - cpu: [x64] - os: [freebsd] - - '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0': - resolution: {integrity: sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==} - engines: {node: '>= 20'} - cpu: [arm] - os: [linux] - - '@tailwindcss/oxide-linux-arm64-gnu@4.3.0': - resolution: {integrity: sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [linux] - - '@tailwindcss/oxide-linux-arm64-musl@4.3.0': - resolution: {integrity: sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [linux] - - '@tailwindcss/oxide-linux-x64-gnu@4.3.0': - resolution: {integrity: sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==} - engines: {node: '>= 20'} - cpu: [x64] - os: [linux] - - '@tailwindcss/oxide-linux-x64-musl@4.3.0': - resolution: {integrity: sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==} - engines: {node: '>= 20'} - cpu: [x64] - os: [linux] - - '@tailwindcss/oxide-wasm32-wasi@4.3.0': - resolution: {integrity: sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==} - 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.0': - resolution: {integrity: sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [win32] - - '@tailwindcss/oxide-win32-x64-msvc@4.3.0': - resolution: {integrity: sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==} - engines: {node: '>= 20'} - cpu: [x64] - os: [win32] - - '@tailwindcss/oxide@4.3.0': - resolution: {integrity: sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==} - engines: {node: '>= 20'} - - '@tailwindcss/postcss@4.3.0': - resolution: {integrity: sha512-Jm05Tjx+9yCLGv5qw1c+84Psds8MnyrEQYCB+FFk2lgGiUjlRqdxke4mVTuYrj2xnVZqKim2Apr5ySuQRYAw/w==} - - '@tailwindcss/typography@0.5.19': - resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==} - peerDependencies: - tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' - - '@tanstack/history@1.162.0': - resolution: {integrity: sha512-79pf/RkhteYZTRgcR4F9kbk84P2N8rugQJswxfIqovlbRiT3yI7eBE+5QorIrZaOKktsgzRlXh1l/du/xpl4iA==} - engines: {node: '>=20.19'} - - '@tanstack/query-core@5.101.4': - resolution: {integrity: sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==} - - '@tanstack/react-query@5.101.4': - resolution: {integrity: sha512-yRg2pfOCxIs4ZJW3XYYHU/WgtD04FHSnfHlpRT7h7pR77hwkdRG4wxbKe4aq6P0RvXUTBSQpQeadS1SUYUe+KA==} - peerDependencies: - react: ^18 || ^19 - - '@tanstack/react-router@1.170.18': - resolution: {integrity: sha512-wpbGYZEp/fmz1q4bn7BD8VZ+/VZ7GBqSJv5V969pU+chP8y7dquWDmKTFMohvUegb9lg12m1uPVvD6kB2wORvQ==} - engines: {node: '>=20.19'} - peerDependencies: - react: '>=18.0.0 || >=19.0.0' - react-dom: '>=18.0.0 || >=19.0.0' - - '@tanstack/react-store@0.9.3': - resolution: {integrity: sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - '@tanstack/react-virtual@3.14.3': - resolution: {integrity: sha512-k/cnHPVaOfn46hSbiY6n4Dzf4QjCGWSF40zR5QIIYUqPAjpA6TN7InfYmcMiDVQGP2iUn9xsRbAl8u1v3UmeVQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - '@tanstack/router-core@1.171.15': - resolution: {integrity: sha512-IILCDcLaItMZQ2jEmCABHY1Nhjjn5XUvwpQp3e4Nmu+vfg0BgYFuu/QASz2SwE2ZNbVMrvt8X/wxa+Gg5aErxA==} - engines: {node: '>=20.19'} - - '@tanstack/store@0.9.3': - resolution: {integrity: sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw==} - - '@tanstack/virtual-core@3.17.1': - resolution: {integrity: sha512-VZyW2Uiml5tmBZwPGrSD3Sz73OxzljQMCmzYHsUTPEuTsERf5xwa+uWb01xEzkz3ZSYTjj8NEb/mKHvgKxyZdA==} - - '@theguild/remark-mermaid@0.3.0': - resolution: {integrity: sha512-Fy1J4FSj8totuHsHFpaeWyWRaRSIvpzGTRoEfnNJc1JmLV9uV70sYE3zcT+Jj5Yw20Xq4iCsiT+3Ho49BBZcBQ==} - peerDependencies: - react: ^18.2.0 || ^19.0.0 - - '@theguild/remark-npm2yarn@0.3.3': - resolution: {integrity: sha512-ma6DvR03gdbvwqfKx1omqhg9May/VYGdMHvTzB4VuxkyS7KzfZ/lzrj43hmcsggpMje0x7SADA/pcMph0ejRnA==} - - '@tree-sitter-grammars/tree-sitter-yaml@0.7.1': - resolution: {integrity: sha512-AynBwkIoQCTgjDR33bDUp9Mqq+YTco0is3n5hRApMqG9of/6A4eQsfC1/uSEeHSUyMQSYawcAWamsexnVpIP4Q==} - peerDependencies: - tree-sitter: ^0.22.4 - peerDependenciesMeta: - tree-sitter: - optional: true - - '@trpc/server@11.16.0': - resolution: {integrity: sha512-XgGuUMddrUTd04+za/WE5GFuZ1/YU9XQG0t3VL5WOIu2JspkOlq6k4RYEiqS6HSJt+S0RXaPdIoE2anIP/BBRQ==} - hasBin: true - peerDependencies: - typescript: '>=5.7.2' - - '@ts-morph/common@0.28.1': - resolution: {integrity: sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g==} - - '@types/chai@5.2.3': - resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - - '@types/d3-array@3.2.2': - resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} - - '@types/d3-axis@3.0.6': - resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} - - '@types/d3-brush@3.0.6': - resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} - - '@types/d3-chord@3.0.6': - resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} - - '@types/d3-color@3.1.3': - resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} - - '@types/d3-contour@3.0.6': - resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} - - '@types/d3-delaunay@6.0.4': - resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} - - '@types/d3-dispatch@3.0.7': - resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==} - - '@types/d3-drag@3.0.7': - resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} - - '@types/d3-dsv@3.0.7': - resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} - - '@types/d3-ease@3.0.2': - resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} - - '@types/d3-fetch@3.0.7': - resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} - - '@types/d3-force@3.0.10': - resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} - - '@types/d3-format@3.0.4': - resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} - - '@types/d3-geo@3.1.0': - resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} - - '@types/d3-hierarchy@3.1.7': - resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} - - '@types/d3-interpolate@3.0.4': - resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} - - '@types/d3-path@3.1.1': - resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} - - '@types/d3-polygon@3.0.2': - resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} - - '@types/d3-quadtree@3.0.6': - resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} - - '@types/d3-random@3.0.3': - resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==} - - '@types/d3-scale-chromatic@3.1.0': - resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} - - '@types/d3-scale@4.0.9': - resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} - - '@types/d3-selection@3.0.11': - resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} - - '@types/d3-shape@3.1.8': - resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} - - '@types/d3-time-format@4.0.3': - resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} - - '@types/d3-time@3.0.4': - resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} - - '@types/d3-timer@3.0.2': - resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} - - '@types/d3-transition@3.0.9': - resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} - - '@types/d3-zoom@3.0.8': - resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} - - '@types/d3@7.4.3': - resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} - - '@types/debug@4.1.12': - resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} - - '@types/deep-eql@4.0.2': - resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - - '@types/dom-speech-recognition@0.0.1': - resolution: {integrity: sha512-udCxb8DvjcDKfk1WTBzDsxFbLgYxmQGKrE/ricoMqHRNjSlSUCcamVTA5lIQqzY10mY5qCY0QDwBfFEwhfoDPw==} - - '@types/estree-jsx@1.0.5': - resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} - - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - - '@types/estree@1.0.9': - resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} - - '@types/geojson@7946.0.16': - resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} - - '@types/google.maps@3.58.1': - resolution: {integrity: sha512-X9QTSvGJ0nCfMzYOnaVs/k6/4L+7F5uCS+4iUmkLEls6J9S/Phv+m/i3mDeyc49ZBgwab3EFO1HEoBY7k98EGQ==} - - '@types/hast@3.0.5': - resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} - - '@types/hogan.js@3.0.5': - resolution: {integrity: sha512-/uRaY3HGPWyLqOyhgvW9Aa43BNnLZrNeQxl2p8wqId4UHMfPKolSB+U7BlZyO1ng7MkLnyEAItsBzCG0SDhqrA==} - - '@types/katex@0.16.7': - resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} - - '@types/mdast@4.0.4': - resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} - - '@types/mdx@2.0.13': - resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} - - '@types/ms@2.1.0': - resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - - '@types/nlcst@2.0.3': - resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} - - '@types/node@22.19.17': - resolution: {integrity: sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==} - - '@types/prismjs@1.26.5': - resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==} - - '@types/qs@6.14.0': - resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} - - '@types/ramda@0.30.2': - resolution: {integrity: sha512-PyzHvjCalm2BRYjAU6nIB3TprYwMNOUY/7P/N8bSzp9W/yM2YrtGtAnnVtaCNSeOZ8DzKyFDvaqQs7LnWwwmBA==} - - '@types/react-dom@19.2.3': - resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} - peerDependencies: - '@types/react': ^19.2.0 - - '@types/react-syntax-highlighter@15.5.13': - resolution: {integrity: sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==} - - '@types/react@19.2.16': - resolution: {integrity: sha512-esJiCAnl0kfpNdE69f3So4WJUXy95dLZydX0KwK46riIHDzHM7O9Vtf9xCHW0PXIqvgqNrswl522kA/5yx+F4w==} - - '@types/trusted-types@2.0.7': - resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} - - '@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==} - - '@types/use-sync-external-store@0.0.6': - resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} - - '@typescript/vfs@1.6.2': - resolution: {integrity: sha512-hoBwJwcbKHmvd2QVebiytN1aELvpk9B74B4L1mFm/XT1Q/VOYAWl2vQ9AWRFtQq8zmz6enTpfTV8WRc4ATjW/g==} - peerDependencies: - typescript: '*' - - '@uidotdev/usehooks@2.4.1': - resolution: {integrity: sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg==} - engines: {node: '>=16'} - peerDependencies: - react: '>=18.0.0' - react-dom: '>=18.0.0' - - '@ungap/structured-clone@1.3.0': - resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - deprecated: Potential CWE-502 - Update to 1.3.1 or higher - - '@upsetjs/venn.js@2.0.0': - resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==} - - '@vercel/oidc@3.2.0': - resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==} - engines: {node: '>= 20'} - - '@vitest/expect@4.1.8': - resolution: {integrity: sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==} - - '@vitest/mocker@4.1.8': - resolution: {integrity: sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==} - peerDependencies: - msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - msw: - optional: true - vite: - optional: true - - '@vitest/pretty-format@4.1.8': - resolution: {integrity: sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==} - - '@vitest/runner@4.1.8': - resolution: {integrity: sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==} - - '@vitest/snapshot@4.1.8': - resolution: {integrity: sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==} - - '@vitest/spy@4.1.8': - resolution: {integrity: sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==} - - '@vitest/utils@4.1.8': - resolution: {integrity: sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==} - - '@workflow/serde@4.1.0': - resolution: {integrity: sha512-pav4F2BoirECWR7Nf1TKt+2eETcBj7jj4cBefQ8VXQCA6NPkaKeLfj/zMgi+3zYV5ZIBT4GuUiphsj0/b9hPQQ==} - - '@xmldom/xmldom@0.9.12': - resolution: {integrity: sha512-5AXjrcMClTryPe9LgZrygpB1lj7s0S9E0+W+AHaVKAVyHanafK86iPSvG5xHVSp/jC+VH1UXu0TAEmY279xH7A==} - engines: {node: '>=14.6'} - - abbrev@1.1.1: - resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - - acorn@8.15.0: - resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} - engines: {node: '>=0.4.0'} - hasBin: true - - agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - - ai@7.0.37: - resolution: {integrity: sha512-stF+SEQJgKY3Qfe3FwNzqUrehHviOp2l7LemoI8YMOa0Zk5PxKsRNgUk3cHXz0RAue9RRyCAis2fz6LSCP6EKw==} - engines: {node: '>=22'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - algoliasearch-helper@3.29.1: - resolution: {integrity: sha512-6ck2YFudF2Pje7szQoPBiRFTGfd+1I+0I/WfLPGn0bj1kvrFoOQmNyedNiDxTk3/r4IfSLDYk+RA4G7u8H6+yA==} - peerDependencies: - algoliasearch: '>= 3.1 < 6' - - algoliasearch@5.53.0: - resolution: {integrity: sha512-OGW1q6b91CRSSeiOnM8LxuR5NYJ2esvw66jUZ4IIvdv+ItNkx3pwLuyR+jaCdbGee4ov5WgUnyPryyh11xvByQ==} - engines: {node: '>= 14.0.0'} - - ansi-escapes@7.2.0: - resolution: {integrity: sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==} - engines: {node: '>=18'} - - ansi-regex@6.2.2: - resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} - engines: {node: '>=12'} - - ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} - engines: {node: '>=12'} - - apg-lite@1.0.5: - resolution: {integrity: sha512-SlI+nLMQDzCZfS39ihzjGp3JNBQfJXyMi6cg9tkLOCPVErgFsUIAEdO9IezR7kbP5Xd0ozcPNQBkf9TO5cHgWw==} - - arg@5.0.2: - resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - - aria-hidden@1.2.6: - resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} - engines: {node: '>=10'} - - array-iterate@2.0.1: - resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} - - assertion-error@2.0.1: - resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} - engines: {node: '>=12'} - - astring@1.9.0: - resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} - hasBin: true - - asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - - autolinker@3.16.2: - resolution: {integrity: sha512-JiYl7j2Z19F9NdTmirENSUUIIL/9MytEWtmzhfmsKPCp9E+G35Y0UNCMoM9tFigxT59qSc8Ml2dlZXOCVTYwuA==} - - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - - axios@1.20.0: - resolution: {integrity: sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==} - - bail@2.0.2: - resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} - - balanced-match@4.0.4: - resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} - engines: {node: 18 || 20 || >=22} - - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - baseline-browser-mapping@2.10.33: - resolution: {integrity: sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw==} - engines: {node: '>=6.0.0'} - hasBin: true - - before-after-hook@4.0.0: - resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} - - better-react-mathjax@2.3.0: - resolution: {integrity: sha512-K0ceQC+jQmB+NLDogO5HCpqmYf18AU2FxDbLdduYgkHYWZApFggkHE4dIaXCV1NqeoscESYXXo1GSkY6fA295w==} - peerDependencies: - react: '>=16.8' - - brace-expansion@5.0.12: - resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} - engines: {node: 20 || >=22} - - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - call-bind@1.0.8: - resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - - caniuse-lite@1.0.30001760: - resolution: {integrity: sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==} - - ccount@2.0.1: - resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - - chai@6.2.2: - resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} - engines: {node: '>=18'} - - chalk@5.6.2: - resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - - 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==} - - citty@0.2.2: - resolution: {integrity: sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==} - - class-variance-authority@0.7.1: - resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} - - classnames@2.5.1: - resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} - - cli-cursor@5.0.0: - resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} - engines: {node: '>=18'} - - cli-spinners@3.3.0: - resolution: {integrity: sha512-/+40ljC3ONVnYIttjMWrlL51nItDAbBrq2upN8BPyvGU/2n5Oxw3tbNwORCaNuNqLJnxGqOfjUuhsv7l5Q4IsQ==} - engines: {node: '>=18.20'} - - cli-truncate@5.1.1: - resolution: {integrity: sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==} - engines: {node: '>=20'} - - client-only@0.0.1: - resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} - - clipboardy@4.0.0: - resolution: {integrity: sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w==} - engines: {node: '>=18'} - - clsx@2.1.1: - resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} - engines: {node: '>=6'} - - cmdk@1.1.1: - resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==} - peerDependencies: - react: ^18 || ^19 || ^19.0.0-rc - react-dom: ^18 || ^19 || ^19.0.0-rc - - code-block-writer@13.0.3: - resolution: {integrity: sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==} - - collapse-white-space@2.1.0: - resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} - - colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - - combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - - comma-separated-tokens@2.0.3: - resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - - commander@13.1.0: - resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} - engines: {node: '>=18'} - - commander@14.0.3: - resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} - engines: {node: '>=20'} - - commander@7.2.0: - resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} - engines: {node: '>= 10'} - - commander@8.3.0: - resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} - engines: {node: '>= 12'} - - compute-scroll-into-view@3.1.1: - resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} - - confbox@0.1.8: - resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} - - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - - cookie-es@3.1.1: - resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} - - copy-to-clipboard@3.3.3: - resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} - - core-js-pure@3.47.0: - resolution: {integrity: sha512-BcxeDbzUrRnXGYIVAGFtcGQVNpFcUhVjr6W7F8XktvQW2iJP9e66GP6xdKotCRFlrxBvNIBrhwKteRXqMV86Nw==} - - core-js@3.47.0: - resolution: {integrity: sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==} - - cose-base@1.0.3: - resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} - - cose-base@2.2.0: - resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} - - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - - css.escape@1.5.1: - resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} - - cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - - csstype@3.2.3: - resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - - cytoscape-cose-bilkent@4.1.0: - resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} - peerDependencies: - cytoscape: ^3.2.0 - - cytoscape-fcose@2.2.0: - resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==} - peerDependencies: - cytoscape: ^3.2.0 - - cytoscape@3.34.2: - resolution: {integrity: sha512-Cm2jaj1X/PBNlzV9yH8zcfGOxO7U+CJ/+mxSBVPSchLaugdp4jtlGx5qaHtPRZ6tgiZ5P+o1XoRfJA+ba6KM3g==} - engines: {node: '>=0.10'} - - d3-array@2.12.1: - resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} - - d3-array@3.2.4: - resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} - engines: {node: '>=12'} - - d3-axis@3.0.0: - resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} - engines: {node: '>=12'} - - d3-brush@3.0.0: - resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} - engines: {node: '>=12'} - - d3-chord@3.0.1: - resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} - engines: {node: '>=12'} - - d3-color@3.1.0: - resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} - engines: {node: '>=12'} - - d3-contour@4.0.2: - resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} - engines: {node: '>=12'} - - d3-delaunay@6.0.4: - resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} - engines: {node: '>=12'} - - d3-dispatch@3.0.1: - resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} - engines: {node: '>=12'} - - d3-drag@3.0.0: - resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} - engines: {node: '>=12'} - - d3-dsv@3.0.1: - resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} - engines: {node: '>=12'} - hasBin: true - - d3-ease@3.0.1: - resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} - engines: {node: '>=12'} - - d3-fetch@3.0.1: - resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} - engines: {node: '>=12'} - - d3-force@3.0.0: - resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} - engines: {node: '>=12'} - - d3-format@3.1.0: - resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} - engines: {node: '>=12'} - - d3-geo@3.1.1: - resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} - engines: {node: '>=12'} - - d3-hierarchy@3.1.2: - resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} - engines: {node: '>=12'} - - d3-interpolate@3.0.1: - resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} - engines: {node: '>=12'} - - d3-path@1.0.9: - resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} - - d3-path@3.1.0: - resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} - engines: {node: '>=12'} - - d3-polygon@3.0.1: - resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} - engines: {node: '>=12'} - - d3-quadtree@3.0.1: - resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} - engines: {node: '>=12'} - - d3-random@3.0.1: - resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} - engines: {node: '>=12'} - - d3-sankey@0.12.3: - resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} - - d3-scale-chromatic@3.1.0: - resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} - engines: {node: '>=12'} - - d3-scale@4.0.2: - resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} - engines: {node: '>=12'} - - d3-selection@3.0.0: - resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} - engines: {node: '>=12'} - - d3-shape@1.3.7: - resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} - - d3-shape@3.2.0: - resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} - engines: {node: '>=12'} - - d3-time-format@4.1.0: - resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} - engines: {node: '>=12'} - - d3-time@3.1.0: - resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} - engines: {node: '>=12'} - - d3-timer@3.0.1: - resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} - engines: {node: '>=12'} - - d3-transition@3.0.1: - resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} - engines: {node: '>=12'} - peerDependencies: - d3-selection: 2 - 3 - - d3-zoom@3.0.0: - resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} - engines: {node: '>=12'} - - d3@7.9.0: - resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} - engines: {node: '>=12'} - - dagre-d3-es@7.0.14: - resolution: {integrity: sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==} - - date-fns@4.4.0: - resolution: {integrity: sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==} - - dayjs@1.11.23: - resolution: {integrity: sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==} - - debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - decimal.js-light@2.5.1: - resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} - - decode-named-character-reference@1.2.0: - resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} - - deep-extend@0.6.0: - resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} - engines: {node: '>=4.0.0'} - - deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - delaunator@5.0.1: - resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} - - delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - - 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==} - - dompurify@3.4.14: - resolution: {integrity: sha512-dVoH9z+MY+C9IilgGCk3YfFqjLi3fChm2OiKJMzh6axrJ5qwxqWaZamgmHrpv22CN/KdbZJuGEGgfQoL00LTdg==} - - dotenv@17.4.2: - resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} - engines: {node: '>=12'} - - drange@1.1.1: - resolution: {integrity: sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==} - engines: {node: '>=4'} - - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - - emoji-regex@10.6.0: - resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} - - enhanced-resolve@5.21.0: - resolution: {integrity: sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==} - engines: {node: '>=10.13.0'} - - entities@6.0.1: - resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} - engines: {node: '>=0.12'} - - environment@1.1.0: - resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} - engines: {node: '>=18'} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-module-lexer@2.0.0: - resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} - - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} - - es-toolkit@1.49.0: - resolution: {integrity: sha512-G5iZ6Pc/FNRY/soKZHC+TxGDD83rHUDXxzaWhGCX44vAv/tMs56WMusnm/KMNK+luUPsgA9U28cGr4RDlSzL2g==} - - 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.1: - resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} - engines: {node: '>=18'} - hasBin: true - - escape-string-regexp@5.0.0: - resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} - engines: {node: '>=12'} - - esm@3.2.25: - resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} - engines: {node: '>=6'} - - 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@2.1.0: - resolution: {integrity: sha512-bEN9VHRyXAUOjkKVQVvArFym08BTWB0aJPppZZr0UNyAqWsLaVfAqP7hbaTJjzHifmB5ebnR8Wm7r7yGN/HonQ==} - - estree-util-is-identifier-name@3.0.0: - resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} - - estree-util-scope@1.0.0: - resolution: {integrity: sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==} - - 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==} - - eventemitter3@5.0.4: - resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} - - eventsource-parser@3.1.0: - resolution: {integrity: sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==} - engines: {node: '>=18.0.0'} - - execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} - - expect-type@1.3.0: - resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} - engines: {node: '>=12.0.0'} - - extend@3.0.2: - resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - - fast-content-type-parse@3.0.0: - resolution: {integrity: sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==} - - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - - fast-json-patch@3.1.1: - resolution: {integrity: sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==} - - fastdom@1.0.12: - resolution: {integrity: sha512-LB+xjSTEbjHE1cWsxu+tN2Xqr1kpi+V9aADI7sVM5ZMaXyYGPHULQMzpJMYqOTULK/73pUkWVzzObFRBkPr+hg==} - - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - - fault@1.0.4: - resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} - - fault@2.0.1: - resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} - - fdir@6.5.0: - resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} - engines: {node: '>=12.0.0'} - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - - fflate@0.4.9: - resolution: {integrity: sha512-zdxgIEddhfsyCaWpJ2SdXEP8ZMrKJ6+5jl4OupODcywU0IhRk6gdXuVGcPICyfx2H97hVK7xmJtRLPjkxAX8Vw==} - - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - - follow-redirects@1.16.0: - resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - - for-each@0.3.5: - resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} - engines: {node: '>= 0.4'} - - form-data@4.0.6: - resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} - engines: {node: '>= 6'} - - format@0.2.2: - resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} - engines: {node: '>=0.4.x'} - - framer-motion@12.40.0: - resolution: {integrity: sha512-uaBd3qC1v3KQqBEjwTUd183K6PbS+j0yR9w9VmEOLWA/tnUcSn8Xa3uck7t4dgpDoUss8xQTcj8W2L07lrnLFg==} - peerDependencies: - '@emotion/is-prop-valid': '*' - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@emotion/is-prop-valid': - optional: true - react: - optional: true - react-dom: - optional: true - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - get-east-asian-width@1.4.0: - resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} - engines: {node: '>=18'} - - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-nonce@1.0.1: - resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} - engines: {node: '>=6'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - - github-slugger@2.0.0: - resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - hachure-fill@0.5.2: - resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} - - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - hasown@2.0.4: - resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} - engines: {node: '>= 0.4'} - - hast-util-from-dom@5.0.1: - resolution: {integrity: sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==} - - hast-util-from-html-isomorphic@2.0.0: - resolution: {integrity: sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==} - - hast-util-from-html@2.0.3: - resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} - - hast-util-from-parse5@8.0.3: - resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} - - hast-util-is-element@3.0.0: - resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} - - hast-util-parse-selector@4.0.0: - resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} - - hast-util-raw@9.1.0: - resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} - - hast-util-sanitize@5.0.2: - resolution: {integrity: sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==} - - 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-to-string@3.0.1: - resolution: {integrity: sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==} - - hast-util-to-text@4.0.2: - resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} - - 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==} - - highlight.js@10.7.3: - resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} - - highlightjs-vue@1.0.0: - resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==} - - hogan.js@3.0.2: - resolution: {integrity: sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg==} - hasBin: true - - htm@3.1.1: - resolution: {integrity: sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ==} - - html-url-attributes@3.0.1: - resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} - - html-void-elements@3.0.0: - resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - - https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} - - human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - - husky@9.1.7: - resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} - engines: {node: '>=18'} - hasBin: true - - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - - immer@10.2.0: - resolution: {integrity: sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==} - - immer@11.1.15: - resolution: {integrity: sha512-VrNANlmnWQnh5COXIIOQXM9oOJw7naGKlBT74ZOOR6lpVXc3gFEu9FJLDFcpCJ2j+NWr8TIwtWD//T6ZX6TKiQ==} - - immutable@4.3.9: - resolution: {integrity: sha512-ObHy4YN7ycwZOUCLI1/6svfyAFu7vL8RhAvVu/bh/RZW9EPlOyDaQ9jDQWCtdqzaXUjgXZCW1migtHE7YI7UGQ==} - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - inline-style-parser@0.2.7: - resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} - - instantsearch-ui-components@0.29.0: - resolution: {integrity: sha512-/7bY63BM+nRs38vL/1hYtjheDlf3FEK2edsnc3UEA3JJZZZk0Bq1Ds0o8WcSVymT/BSZz8DNb87eLDTz6gDVcw==} - - instantsearch.js@4.100.0: - resolution: {integrity: sha512-8fomIg5pwSTDDQQL/yUNWdc0lvvw+1xhjgNlWJwgmRsonZ3GlLT00bvznfrTHq0IRw9/Z894uMQEpH0t/l742A==} - peerDependencies: - algoliasearch: '>= 3.1 < 6' - - internmap@1.0.1: - resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} - - internmap@2.0.3: - resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} - engines: {node: '>=12'} - - invariant@2.2.4: - resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} - - 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-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - - is-decimal@2.0.1: - resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} - - is-docker@3.0.0: - resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - hasBin: true - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-fullwidth-code-point@5.1.0: - resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} - engines: {node: '>=18'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - is-hexadecimal@2.0.1: - resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} - - is-inside-container@1.0.0: - resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} - engines: {node: '>=14.16'} - hasBin: true - - is-interactive@2.0.0: - resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} - engines: {node: '>=12'} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} - engines: {node: '>=12'} - - is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - is-typed-array@1.1.15: - resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} - engines: {node: '>= 0.4'} - - is-unicode-supported@2.1.0: - resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} - engines: {node: '>=18'} - - is-wsl@3.1.0: - resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} - engines: {node: '>=16'} - - is64bit@2.0.0: - resolution: {integrity: sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw==} - engines: {node: '>=18'} - - isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - - isbot@5.2.1: - resolution: {integrity: sha512-dJ+LpKyClQZ7NG+j3OensC/mAZkGpukE9YUrgPYvAZj2doVL0edfDgywTUh5CXa0o+nW9a1V9e5+CJTX8+SxRw==} - engines: {node: '>=18'} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - jiti@2.6.1: - resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} - hasBin: true - - js-file-download@0.4.12: - resolution: {integrity: sha512-rML+NkoD08p5Dllpjo0ffy4jRHeY6Zsapvr/W86N7E0yuzAO6qa5X9+xog6zQNlH102J7IXljNY2FtS6Lj3ucg==} - - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - js-yaml@4.3.2: - resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} - hasBin: true - - json-schema-to-ts@3.1.1: - resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==} - engines: {node: '>=16'} - - json-schema@0.4.0: - resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} - - jsonc-parser@3.3.1: - resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} - - katex@0.16.27: - resolution: {integrity: sha512-aeQoDkuRWSqQN6nSvVCEFvfXdqo1OQiCmmW1kc9xSdjutPv7BGO7pqY9sQRJpMOGrEdfDgF2TfRXe5eUAD2Waw==} - hasBin: true - - katex@0.16.47: - resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==} - hasBin: true - - khroma@2.1.0: - resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} - - layout-base@1.0.2: - resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} - - layout-base@2.0.1: - resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} - - 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] - - lightningcss-linux-arm64-musl@1.32.0: - resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - - lightningcss-linux-x64-gnu@1.32.0: - resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - - 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] - - 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'} - - lint-staged@16.4.0: - resolution: {integrity: sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==} - engines: {node: '>=20.17'} - hasBin: true - - listr2@9.0.5: - resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} - engines: {node: '>=20.0.0'} - - lodash-es@4.18.1: - resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} - - lodash.debounce@4.0.8: - resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} - - lodash@4.18.1: - resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} - - log-symbols@7.0.1: - resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} - engines: {node: '>=18'} - - log-update@6.1.0: - resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} - engines: {node: '>=18'} - - long@5.3.2: - resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} - - longest-streak@3.1.0: - resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - - loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - - lowlight@1.20.0: - resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} - - lucide-react@0.577.0: - resolution: {integrity: sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A==} - 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==} - - 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==} - - markdown-to-jsx@7.7.17: - resolution: {integrity: sha512-7mG/1feQ0TX5I7YyMZVDgCC/y2I3CiEhIRQIhyov9nGBP5eoVrOXXHuL5ZP8GRfxVZKRiXWJgwXkb9It+nQZfQ==} - engines: {node: '>= 10'} - peerDependencies: - react: '>= 0.14.0' - peerDependenciesMeta: - react: - optional: true - - marked@16.4.2: - resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==} - engines: {node: '>= 20'} - hasBin: true - - marked@17.0.3: - resolution: {integrity: sha512-jt1v2ObpyOKR8p4XaUJVk3YWRJ5n+i4+rjQopxvV32rSndTJXvIzuUdWWIy/1pFQMkQmvTXawzDNqOH/CUmx6A==} - engines: {node: '>= 20'} - hasBin: true - - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - - mathjax-full@3.2.2: - resolution: {integrity: sha512-+LfG9Fik+OuI8SLwsiR02IVdjcnRCy5MufYLi0C3TdMT56L/pjB0alMVGgoWJF8pN9Rc7FESycZB9BMNWIid5w==} - deprecated: Version 4 replaces this package with the scoped package @mathjax/src - - mdast-util-find-and-replace@3.0.2: - resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} - - mdast-util-from-markdown@2.0.2: - resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} - - mdast-util-frontmatter@2.0.1: - resolution: {integrity: sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==} - - 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-math@3.0.0: - resolution: {integrity: sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==} - - 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-newline-to-break@2.0.0: - resolution: {integrity: sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==} - - 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==} - - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - mermaid@11.17.2: - resolution: {integrity: sha512-V6K3C8EBdEsPFZXSKMJe6ppQOENxuHARr9GvHX4hh47lAbhMRD9qf4oEK7LoaRQxULMa80/qt5gHO73aCleBBg==} - - mhchemparser@4.2.1: - resolution: {integrity: sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==} - - micromark-core-commonmark@2.0.3: - resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} - - micromark-extension-frontmatter@2.0.0: - resolution: {integrity: sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==} - - 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-math@3.1.0: - resolution: {integrity: sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==} - - 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==} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - - mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - - mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - - mimic-function@5.0.1: - resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} - engines: {node: '>=18'} - - minim@0.23.8: - resolution: {integrity: sha512-bjdr2xW1dBCMsMGGsUeqM4eFI60m94+szhxWys+B1ztIt6gWSfeGBdSVCIawezeHYLYn0j6zrsXdQS/JllBzww==} - engines: {node: '>=6'} - - minimatch@10.2.6: - resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} - engines: {node: 18 || 20 || >=22} - - mj-context-menu@0.6.1: - resolution: {integrity: sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA==} - - mkdirp@0.3.0: - resolution: {integrity: sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew==} - deprecated: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.) - - mlly@1.8.0: - resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} - - motion-dom@12.40.0: - resolution: {integrity: sha512-HxU3ZaBwNPVQUBQf1xxgq+7JrPNZvjLVxgbpEZL7RrWJnsxOf0/OM+yrHG9ogLQ31Do/r57Oz2gQWPK+6q62mg==} - - motion-utils@12.39.0: - resolution: {integrity: sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==} - - motion@12.40.0: - resolution: {integrity: sha512-yjrHUrBFW6kQvjJwRsoiPSAhC5tRwRqNGJWmiJ4CrGnbKp0V88AdzkhBmDoqIsIPfarOe0Uddd37Xq43/gIocA==} - peerDependencies: - '@emotion/is-prop-valid': '*' - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@emotion/is-prop-valid': - optional: true - 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 - - negotiator@1.0.0: - resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} - engines: {node: '>= 0.6'} - - neotraverse@1.0.1: - resolution: {integrity: sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w==} - engines: {node: '>= 10'} - - 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-validate-link@1.6.6: - resolution: {integrity: sha512-Ep96TQ2zkaVi/ekByURk1fT75xymRfai+SCPEjX/ld64AA+dMjqhsDxERbdDIKONZGcHTypEE27fOY7eyMZIdw==} - peerDependencies: - '@react-router/dev': 7.x.x - '@types/mdast': '*' - '@types/unist': '*' - peerDependenciesMeta: - '@react-router/dev': - optional: true - '@types/mdast': - optional: true - '@types/unist': - optional: true - - next@16.3.8: - resolution: {integrity: sha512-U7QEZaTini6wKrb8A8hqLLqYQyCetegKjCpJOyxk642vWoMoU1x5PyZCJFvgYgiptA8xc5j/9xYlZFO7w9Sjmw==} - 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 - - nextra-theme-docs@4.6.1: - resolution: {integrity: sha512-u5Hh8erVcGOXO1FVrwYBgrEjyzdYQY0k/iAhLd8RofKp+Bru3fyLy9V9W34mfJ0KHKHjv/ldlDTlb4KlL4eIuQ==} - peerDependencies: - next: '>=14' - nextra: 4.6.1 - react: '>=18' - react-dom: '>=18' - - nextra@4.6.1: - resolution: {integrity: sha512-yz5WMJFZ5c58y14a6Rmwt+SJUYDdIgzWSxwtnpD4XAJTq3mbOqOg3VTaJqLiJjwRSxoFRHNA1yAhnhbvbw9zSg==} - engines: {node: '>=18'} - peerDependencies: - next: '>=14' - react: '>=18' - react-dom: '>=18' - - nlcst-to-string@4.0.0: - resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==} - - node-abort-controller@3.1.1: - resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} - - node-addon-api@8.5.0: - resolution: {integrity: sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==} - engines: {node: ^18 || ^20 || >= 21} - - node-gyp-build@4.8.4: - resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} - hasBin: true - - nopt@1.0.10: - resolution: {integrity: sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==} - hasBin: true - - npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - npm-to-yarn@3.0.1: - resolution: {integrity: sha512-tt6PvKu4WyzPwWUzy/hvPFqn+uwXO0K1ZHka8az3NnrhWJDmSqI8ncWq0fkL0k/lmmi5tAC11FXwXuh0rFbt1A==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - nypm@0.6.5: - resolution: {integrity: sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==} - engines: {node: '>=18'} - hasBin: true - - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - obug@2.1.1: - resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} - - onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} - - onetime@7.0.0: - resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} - engines: {node: '>=18'} - - 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==} - - openai@6.41.0: - resolution: {integrity: sha512-IGWPopZq6Rjoynjfb3NSLf/z2MTw7UiOsm9TAjPGAjUESH7Uq41Trg4QWehBEn58p74i+m7uoRPV2vXcpPXhyA==} - peerDependencies: - ws: ^8.18.0 - zod: ^3.25 || ^4.0 - peerDependenciesMeta: - ws: - optional: true - zod: - optional: true - - openapi-path-templating@2.2.1: - resolution: {integrity: sha512-eN14VrDvl/YyGxxrkGOHkVkWEoPyhyeydOUrbvjoz8K5eIGgELASwN1eqFOJ2CTQMGCy2EntOK1KdtJ8ZMekcg==} - engines: {node: '>=12.20.0'} - - openapi-server-url-templating@1.3.0: - resolution: {integrity: sha512-DPlCms3KKEbjVQb0spV6Awfn6UWNheuG/+folQPzh/wUaKwuqvj8zt5gagD7qoyxtE03cIiKPgLFS3Q8Bz00uQ==} - engines: {node: '>=12.20.0'} - - ora@9.4.0: - resolution: {integrity: sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ==} - engines: {node: '>=20'} - - package-manager-detector@1.6.0: - resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} - - parse-entities@4.0.2: - resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} - - parse-latin@7.0.0: - resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==} - - parse-numeric-range@1.3.0: - resolution: {integrity: sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==} - - parse5@7.3.0: - resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - - path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - - path-data-parser@0.1.0: - resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} - engines: {node: '>=8.6'} - - picomatch@4.0.4: - resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} - engines: {node: '>=12'} - - picomatch@4.0.5: - resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} - engines: {node: '>=12'} - - pkce-challenge@5.0.1: - resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} - engines: {node: '>=16.20.0'} - - pkg-types@1.3.1: - resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} - - points-on-curve@0.2.0: - resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} - - points-on-path@0.2.1: - resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} - - possible-typed-array-names@1.1.0: - resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} - engines: {node: '>= 0.4'} - - postcss-selector-parser@6.0.10: - resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} - engines: {node: '>=4'} - - postcss@8.5.26: - resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} - engines: {node: ^10 || ^12 || >=14} - - posthog-js@1.379.0: - resolution: {integrity: sha512-cSESmnPvIaY/pyhV5a6pF00q8tqmXlJBQWX9T7dbBh9TXhVM5oQVi7g77vWGD9G8wq4iNF5ZOxqeAK9dpOSOYg==} - - posthog-node@5.35.12: - resolution: {integrity: sha512-WLbeUdSDpERDENBgaDahMo9sqwjo8Gc1f1dnM01HciwdP8OnGlbcVs8zrqjz6Z9GvBnTnRTa0Q2bBQeXrBIeHg==} - engines: {node: ^20.20.0 || >=22.22.0} - peerDependencies: - rxjs: ^7.0.0 - peerDependenciesMeta: - rxjs: - optional: true - - preact@10.29.1: - resolution: {integrity: sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg==} - - prismjs@1.30.0: - resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} - engines: {node: '>=6'} - - prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - - property-information@7.1.0: - resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} - - protobufjs@7.6.5: - resolution: {integrity: sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==} - engines: {node: '>=12.0.0'} - - proxy-from-env@2.1.0: - resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} - engines: {node: '>=10'} - - qs@6.16.0: - resolution: {integrity: sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==} - engines: {node: '>=0.6'} - - query-selector-shadow-dom@1.0.1: - resolution: {integrity: sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==} - - querystringify@2.2.0: - resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - ramda-adjunct@5.1.0: - resolution: {integrity: sha512-8qCpl2vZBXEJyNbi4zqcgdfHtcdsWjOGbiNSEnEBrM6Y0OKOT8UxJbIVGm1TIcjaSu2MxaWcgtsNlKlCk7o7qg==} - engines: {node: '>=0.10.3'} - peerDependencies: - ramda: '>= 0.30.0' - - ramda@0.30.1: - resolution: {integrity: sha512-tEF5I22zJnuclswcZMc8bDIrwRHRzf+NqVEmqg50ShAZMP7MWeR/RGDthfM/p+BlqvF2fXAzpn8i+SJcYD3alw==} - - randexp@0.5.3: - resolution: {integrity: sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==} - engines: {node: '>=4'} - - randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - - react-compiler-runtime@19.1.0-rc.3: - resolution: {integrity: sha512-Cssogys2XZu6SqxRdX2xd8cQAf57BBvFbLEBlIa77161lninbKUn/EqbecCe7W3eqDQfg3rIoOwzExzgCh7h/g==} - peerDependencies: - react: ^17.0.0 || ^18.0.0 || ^19.0.0 || ^0.0.0-experimental - - react-copy-to-clipboard@5.1.1: - resolution: {integrity: sha512-s+HrzLyJBxrpGTYXF15dTgMjAJpEPZT/Yp6NytAtZMRngejxt6Pt5WrfFxLAcsqUDU6sY1Jz6tyHwIicE1U2Xg==} - peerDependencies: - react: '>=15.3.0' - - react-day-picker@10.0.1: - resolution: {integrity: sha512-eNh6BlwcYInWaJtRv18mXQ06Ys/H6rdTZAnTaSdOYJuTpwP1JMCHNd1FDRadA+gbeinq+psdULN5Xnowy9mV8w==} - engines: {node: '>=18'} - peerDependencies: - '@types/react': '>=16.8.0' - react: '>=16.8.0' - peerDependenciesMeta: - '@types/react': - optional: true - - react-debounce-input@3.3.0: - resolution: {integrity: sha512-VEqkvs8JvY/IIZvh71Z0TC+mdbxERvYF33RcebnodlsUZ8RSgyKe2VWaHXv4+/8aoOgXLxWrdsYs2hDhcwbUgA==} - peerDependencies: - react: ^15.3.0 || 16 || 17 || 18 - - react-dom@19.2.7: - resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==} - peerDependencies: - react: ^19.2.7 - - react-hook-form@7.77.0: - resolution: {integrity: sha512-Sslh9YDYc0GDlWT/lxasnIduNo4v3yyvqRGvmGKUre5AFjDs/HV9/OafHGD8d+sB2yoL4UIL9L8X9i0WlZZebg==} - engines: {node: '>=18.0.0'} - peerDependencies: - react: ^16.8.0 || ^17 || ^18 || ^19 - - react-immutable-proptypes@2.2.0: - resolution: {integrity: sha512-Vf4gBsePlwdGvSZoLSBfd4HAP93HDauMY4fDjXhreg/vg6F3Fj/MXDNyTbltPC/xZKmZc+cjLu3598DdYK6sgQ==} - peerDependencies: - immutable: '>=3.6.2' - - react-immutable-pure-component@2.2.2: - resolution: {integrity: sha512-vkgoMJUDqHZfXXnjVlG3keCxSO/U6WeDQ5/Sl0GK2cH8TOxEzQ5jXqDXHEL/jqk6fsNxV05oH5kD7VNMUE2k+A==} - peerDependencies: - immutable: '>= 2 || >= 4.0.0-rc' - react: '>= 16.6' - react-dom: '>= 16.6' - - react-inspector@6.0.2: - resolution: {integrity: sha512-x+b7LxhmHXjHoU/VrFAzw5iutsILRoYyDq97EDYdFpPLcvqtEzk4ZSZSQjnFPbr5T57tLXnHcqFYoN1pI6u8uQ==} - peerDependencies: - react: ^16.8.4 || ^17.0.0 || ^18.0.0 - - react-instantsearch-core@7.34.0: - resolution: {integrity: sha512-TnQdiIR44hHn/3Ef187Bu/bnTKxwk/Wi6zyDgft0pcXfjjGu4/7Y8X1FA27akXU20XzK3R+E2uizjOB50EBdRQ==} - peerDependencies: - algoliasearch: '>= 3.1 < 6' - react: '>= 16.8.0 < 20' - - react-instantsearch@7.34.0: - resolution: {integrity: sha512-qQ2tKQUyxluyb9ACjXXKiLzAnlcyELPuLMmkF/gCQXe3Sr7SKKhoabW+oEQHTNsBQBeOKRnxKGiLbECZ+VyKzw==} - peerDependencies: - algoliasearch: '>= 3.1 < 6' - react: '>= 16.8.0 < 20' - react-dom: '>= 16.8.0 < 20' - - react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - - react-markdown@10.1.0: - resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} - peerDependencies: - '@types/react': '>=18' - react: '>=18' - - react-medium-image-zoom@5.4.0: - resolution: {integrity: sha512-BsE+EnFVQzFIlyuuQrZ9iTwyKpKkqdFZV1ImEQN573QPqGrIUuNni7aF+sZwDcxlsuOMayCr6oO/PZR/yJnbRg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - react-redux@9.3.0: - resolution: {integrity: sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==} - peerDependencies: - '@types/react': ^18.2.25 || ^19 - react: ^18.0 || ^19 - redux: ^5.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - redux: - optional: true - - 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-resizable-panels@4.11.2: - resolution: {integrity: sha512-+kfFbDZ8mygc7g0vxOcDzCVGuwiIUOnILqPoUHo6/uP+Mmyx6HzZU+kj1aOPDlktXuobYbr6BtQekvJwHRX4Eg==} - peerDependencies: - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - - react-shiki@0.10.1: - resolution: {integrity: sha512-mQ6CXwQ6mEIsQvK30+HRONZsiUKGAOubaVCfXCe0Ejsp7P6U1QDpayOHyAkcMTzSLvgfPnCkny399EK7Mb46Sg==} - peerDependencies: - '@types/react': '>=16.14.0' - '@types/react-dom': '>=16.14.0' - react: '>=16.14.0' - react-dom: '>=16.14.0' - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - 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-syntax-highlighter@16.1.1: - resolution: {integrity: sha512-PjVawBGy80C6YbC5DDZJeUjBmC7skaoEUdvfFQediQHgCL7aKyVHe57SaJGfQsloGDac+gCpTfRdtxzWWKmCXA==} - engines: {node: '>= 16.20.2'} - peerDependencies: - react: '>= 0.14.0' - - react@19.2.7: - resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} - engines: {node: '>=0.10.0'} - - reading-time@1.5.0: - resolution: {integrity: sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==} - - recharts@3.6.0: - resolution: {integrity: sha512-L5bjxvQRAe26RlToBAziKUB7whaGKEwD3znoM6fz3DrTowCIC/FnJYnuq1GEzB8Zv2kdTfaxQfi5GoH0tBinyg==} - engines: {node: '>=18'} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-is: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.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==} - - redux-immutable@4.0.0: - resolution: {integrity: sha512-SchSn/DWfGb3oAejd+1hhHx01xUoxY+V7TeK0BKqpkLKiQPVFf7DYzEaKmrEVxsWxielKfSK9/Xq66YyxgR1cg==} - peerDependencies: - immutable: ^3.8.1 || ^4.0.0-rc.1 - - redux-thunk@3.1.0: - resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} - peerDependencies: - redux: ^5.0.0 - - redux@5.0.1: - resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} - - refractor@5.0.0: - resolution: {integrity: sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==} - - 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-harden@1.1.8: - resolution: {integrity: sha512-Qn7vR1xrf6fZCrkm9TDWi/AB4ylrHy+jqsNm1EHOAmbARYA6gsnVJBq/sdBh6kmT4NEZxH5vgIjrscefJAOXcw==} - - rehype-katex@7.0.1: - resolution: {integrity: sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==} - - rehype-parse@9.0.1: - resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==} - - rehype-pretty-code@0.14.1: - resolution: {integrity: sha512-IpG4OL0iYlbx78muVldsK86hdfNoht0z63AP7sekQNW2QOTmjxB7RbTO+rhIYNGRljgHxgVZoPwUl6bIC9SbjA==} - engines: {node: '>=18'} - peerDependencies: - shiki: ^1.0.0 || ^2.0.0 || ^3.0.0 - - rehype-raw@7.0.0: - resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} - - rehype-recma@1.0.0: - resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==} - - rehype-sanitize@6.0.0: - resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==} - - remark-breaks@4.0.0: - resolution: {integrity: sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==} - - remark-frontmatter@5.0.0: - resolution: {integrity: sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==} - - remark-gfm@4.0.1: - resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} - - remark-math@6.0.0: - resolution: {integrity: sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==} - - remark-mdx@3.1.1: - resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==} - - remark-parse@11.0.0: - resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} - - remark-reading-time@2.0.2: - resolution: {integrity: sha512-ILjIuR0dQQ8pELPgaFvz7ralcSN62rD/L1pTUJgWb4gfua3ZwYEI8mnKGxEQCbrXSUF/OvycTkcUbifGOtOn5A==} - - remark-rehype@11.1.2: - resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} - - remark-smartypants@3.0.2: - resolution: {integrity: sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==} - engines: {node: '>=16.0.0'} - - remark-stringify@11.0.0: - resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} - - remark@15.0.1: - resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==} - - remarkable@2.0.1: - resolution: {integrity: sha512-YJyMcOH5lrR+kZdmB0aJJ4+93bEojRZ1HGDn9Eagu6ibg7aVZhc3OWbbShRid+Q5eAfsEqWxpe+g5W5nYNfNiA==} - engines: {node: '>= 6.0.0'} - hasBin: true - - remend@1.3.0: - resolution: {integrity: sha512-iIhggPkhW3hFImKtB10w0dz4EZbs28mV/dmbcYVonWEJ6UGHHpP+bFZnTh6GNWJONg5m+U56JrL+8IxZRdgWjw==} - - repeat-string@1.6.1: - resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} - engines: {node: '>=0.10'} - - requires-port@1.0.0: - resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - - reselect@5.1.1: - resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} - - reselect@5.2.0: - resolution: {integrity: sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==} - - restore-cursor@5.1.0: - resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} - engines: {node: '>=18'} - - ret@0.2.2: - resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} - engines: {node: '>=4'} - - retext-latin@4.0.0: - resolution: {integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==} - - retext-smartypants@6.2.0: - resolution: {integrity: sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==} - - retext-stringify@4.0.0: - resolution: {integrity: sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==} - - retext@9.0.0: - resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==} - - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - - robust-predicates@3.0.2: - resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} - - rollup@4.62.2: - resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - roughjs@4.6.6: - resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - rw@1.3.3: - resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - 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==} - - search-insights@2.17.3: - resolution: {integrity: sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==} - - semver@7.8.5: - resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} - engines: {node: '>=10'} - hasBin: true - - serialize-error@8.1.0: - resolution: {integrity: sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==} - engines: {node: '>=10'} - - seroval-plugins@1.6.8: - resolution: {integrity: sha512-N7mWAMydj89EnYTHtRpS3LBrAz3J9O6oVrqpuXuvlAHfAUG8WEKqtbAd7SBiJNY5NfCAFhSdyAh7wI4Shjqy4g==} - engines: {node: '>=10'} - peerDependencies: - seroval: 1.6.8 - - seroval@1.6.8: - resolution: {integrity: sha512-HlSgSAkTk4EqHcje1ptJjfZi1YDv5KbhVJ/d3P7T/nAXua2VmDu+AKDX5VTdFfZf48nDkWB2TKYt0DrCSa+3wg==} - engines: {node: '>=10'} - - server-only@0.0.1: - resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} - - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - sha.js@2.4.12: - resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==} - engines: {node: '>= 0.10'} - hasBin: true - - sharp@0.35.5: - resolution: {integrity: sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==} - engines: {node: '>=20.9.0'} - peerDependencies: - '@types/node': '*' - peerDependenciesMeta: - '@types/node': - optional: true - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - shiki@3.20.0: - resolution: {integrity: sha512-kgCOlsnyWb+p0WU+01RjkCH+eBVsjL1jOwUYWv0YDWkM2/A46+LDKVs5yZCUXjJG6bj4ndFoAg5iLIIue6dulg==} - - shiki@4.1.0: - resolution: {integrity: sha512-l/ABZPUR5v70jI10EzqfMS/I96vjSGv2y0ihUV+WYFzv0EfvW4s54m0Lg8wCrrL+2IkwBzFTuxkZjPf8b2NX9Q==} - engines: {node: '>=20'} - - short-unique-id@5.3.2: - resolution: {integrity: sha512-KRT/hufMSxXKEDSQujfVE0Faa/kZ51ihUcZQAcmP04t00DvPj7Ox5anHke1sJYUtzSuiT/Y5uyzg/W7bBEGhCg==} - hasBin: true - - side-channel-list@1.0.1: - resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.1: - resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} - engines: {node: '>= 0.4'} - - siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - - slash@5.1.0: - resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} - engines: {node: '>=14.16'} - - slice-ansi@7.1.2: - resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} - engines: {node: '>=18'} - - source-map-js@1.2.2: - resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} - engines: {node: '>=0.10.0'} - - source-map@0.7.6: - resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} - engines: {node: '>= 12'} - - space-separated-tokens@2.0.2: - resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - - speech-rule-engine@4.1.2: - resolution: {integrity: sha512-S6ji+flMEga+1QU79NDbwZ8Ivf0S/MpupQQiIC0rTpU/ZTKgcajijJJb1OcByBQDjrXCN1/DJtGz4ZJeBMPGJw==} - hasBin: true - - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - - stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - - std-env@4.1.0: - resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} - - stdin-discarder@0.3.2: - resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==} - engines: {node: '>=18'} - - streamdown@2.5.0: - resolution: {integrity: sha512-/tTnURfIOxZK/pqJAxsfCvETG/XCJHoWnk3jq9xLcuz6CSpnjjuxSRBTTL4PKGhxiZQf0lqPxGhImdpwcZ2XwA==} - peerDependencies: - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - - strictdom@1.0.1: - resolution: {integrity: sha512-cEmp9QeXXRmjj/rVp9oyiqcvyocWab/HaoN4+bwFeZ7QzykJD6L3yD4v12K1x0tHpqRqVpJevN3gW7kyM39Bqg==} - - string-argv@0.3.2: - resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} - engines: {node: '>=0.6.19'} - - string-width@7.2.0: - resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} - engines: {node: '>=18'} - - string-width@8.1.0: - resolution: {integrity: sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==} - engines: {node: '>=20'} - - stringify-entities@4.0.4: - resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} - - strip-ansi@7.1.2: - resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} - engines: {node: '>=12'} - - strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - - 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 - - stylis@4.3.6: - resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} - - swagger-client@3.38.0: - resolution: {integrity: sha512-n7aykm1BEdQ3fKePJJx63UGjYe8/5fuxFMi3qZP4OJGZvzljKvmhxNwIF/MB71sF/lop9NeWZReKvPib9CY+2g==} - engines: {node: '>=22'} - - swagger-ui-react@5.32.14: - resolution: {integrity: sha512-6LAVBeC78DplbJ7kutm/YeBYo22nPzGOca4bIZAvQG4w2eSetnYDdazaUfY0qzQUlg/H90HnYZX3rg67EmENOw==} - peerDependencies: - react: '>=16.8.0 <20' - react-dom: '>=16.8.0 <20' - - swr@2.4.2: - resolution: {integrity: sha512-ej644Y2bvkIajfR32KGeSSdBXQW+ScjGjkybZgSE7kFpk9eGnV44XY9FJylXi+W75pavSX1PVNB57W5EbhGIYw==} - peerDependencies: - react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - system-architecture@0.1.0: - resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==} - engines: {node: '>=18'} - - tabbable@6.3.0: - resolution: {integrity: sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==} - - tailwind-merge@3.6.0: - resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==} - - tailwindcss-animate@1.0.7: - resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} - peerDependencies: - tailwindcss: '>=3.0.0 || insiders' - - tailwindcss@4.3.0: - resolution: {integrity: sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==} - - tapable@2.3.3: - resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} - engines: {node: '>=6'} - - third-party-capital@1.0.20: - resolution: {integrity: sha512-oB7yIimd8SuGptespDAZnNkzIz+NWaJCu2RMsbs4Wmp9zSDUM8Nhi3s2OOcqYuv3mN4hitXc8DVx+LyUmbUDiA==} - - throttleit@2.1.0: - resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} - engines: {node: '>=18'} - - tiny-invariant@1.3.3: - resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} - - tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - - tinyexec@1.1.1: - resolution: {integrity: sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==} - engines: {node: '>=18'} - - tinyglobby@0.2.17: - resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} - engines: {node: '>=12.0.0'} - - tinyrainbow@3.1.0: - resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} - engines: {node: '>=14.0.0'} - - title@4.0.1: - resolution: {integrity: sha512-xRnPkJx9nvE5MF6LkB5e8QJjE2FW8269wTu/LQdf7zZqBgPly0QJPf/CWAo7srj5so4yXfoLEdCFgurlpi47zg==} - hasBin: true - - to-buffer@1.2.2: - resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} - engines: {node: '>= 0.4'} - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - toggle-selection@1.0.6: - resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} - - tree-sitter-json@0.24.8: - resolution: {integrity: sha512-Tc9ZZYwHyWZ3Tt1VEw7Pa2scu1YO7/d2BCBbKTx5hXwig3UfdQjsOPkPyLpDJOn/m1UBEWYAtSdGAwCSyagBqQ==} - peerDependencies: - tree-sitter: ^0.21.1 - peerDependenciesMeta: - tree-sitter: - optional: true - - tree-sitter@0.21.1: - resolution: {integrity: sha512-7dxoA6kYvtgWw80265MyqJlkRl4yawIjO7S5MigytjELkX43fV2WsAXzsNfO7sBpPPCF5Gp0+XzHk0DwLCq3xQ==} - - tree-sitter@0.22.4: - resolution: {integrity: sha512-usbHZP9/oxNsUY65MQUsduGRqDHQOou1cagUSwjhoSYAmSahjQDAVsh9s+SlZkn8X8+O1FULRGwHu7AFP3kjzg==} - - trim-lines@3.0.1: - resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} - - trough@2.2.0: - resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - - trpc-cli@0.12.4: - resolution: {integrity: sha512-Yo2Ob5J7hUZSWZ2A1M9Kb+0qfSxwmcmYIs3kkQyLd3sn0qU4ryGzNsySfrY3+urqp6FnDnIIdbCSqC9BKxK6Ag==} - engines: {node: '>=18'} - hasBin: true - peerDependencies: - '@orpc/server': ^1.0.0 - '@trpc/server': ^10.45.2 || ^11.0.1 - '@valibot/to-json-schema': ^1.1.0 - effect: ^3.14.2 || ^4.0.0 - valibot: ^1.1.0 - zod: ^3.24.0 || ^4.0.0 - peerDependenciesMeta: - '@orpc/server': - optional: true - '@trpc/server': - optional: true - '@valibot/to-json-schema': - optional: true - effect: - optional: true - valibot: - optional: true - zod: - optional: true - - ts-algebra@2.0.0: - resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} - - ts-dedent@2.2.0: - resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} - engines: {node: '>=6.10'} - - ts-mixer@6.0.4: - resolution: {integrity: sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==} - - ts-morph@27.0.2: - resolution: {integrity: sha512-fhUhgeljcrdZ+9DZND1De1029PrE+cMkIP7ooqkLRTrRLTqcki2AstsyJm0vRNbTbVCNJ0idGlbBrfqc7/nA8w==} - - ts-toolbelt@9.6.0: - resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - tsx@4.23.8: - resolution: {integrity: sha512-8W675THjbzfFmLOQzjDBIBna+WjqMGIxmSZ1mMc1+o9qoVsEuAgQu5j5ueLhau8inOkDu9OslVg0FmfBs1RIHw==} - engines: {node: '>=18.0.0'} - hasBin: true - - twoslash-protocol@0.3.4: - resolution: {integrity: sha512-HHd7lzZNLUvjPzG/IE6js502gEzLC1x7HaO1up/f72d8G8ScWAs9Yfa97igelQRDl5h9tGcdFsRp+lNVre1EeQ==} - - twoslash@0.3.4: - resolution: {integrity: sha512-RtJURJlGRxrkJmTcZMjpr7jdYly1rfgpujJr1sBM9ch7SKVht/SjFk23IOAyvwT1NLCk+SJiMrvW4rIAUM2Wug==} - peerDependencies: - typescript: ^5.5.0 - - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - - typed-array-buffer@1.0.3: - resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} - engines: {node: '>= 0.4'} - - types-ramda@0.30.1: - resolution: {integrity: sha512-1HTsf5/QVRmLzcGfldPFvkVsAdi1db1BBKzi7iW3KBUlOICg/nKnFS+jGqDJS3YD8VsWbAh7JiHeBvbsw8RPxA==} - - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} - hasBin: true - - ufo@1.6.1: - resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} - - ultracite@6.1.0: - resolution: {integrity: sha512-CHdtZbG9MNIcIZ7fCB3SlNbaeH5tmEmOjKThjqongviMsJY5uFxtNBSQhYbj/ExX81mehp1QY2NVKkTiC4CNVA==} - hasBin: true - - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - - unified@11.0.5: - resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} - - unist-util-find-after@5.0.0: - resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} - - unist-util-is@5.2.1: - resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==} - - unist-util-is@6.0.1: - resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} - - unist-util-modify-children@4.0.0: - resolution: {integrity: sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==} - - unist-util-position-from-estree@2.0.0: - resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} - - unist-util-position@5.0.0: - resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} - - unist-util-remove-position@5.0.0: - resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} - - unist-util-remove@4.0.0: - resolution: {integrity: sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg==} - - unist-util-stringify-position@4.0.0: - resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - - unist-util-visit-children@3.0.0: - resolution: {integrity: sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==} - - unist-util-visit-parents@4.1.1: - resolution: {integrity: sha512-1xAFJXAKpnnJl8G7K5KgU7FY55y3GcLIXqkzUj5QF/QVP7biUm0K0O2oqVkYsdjzJKifYeWn9+o6piAK2hGSHw==} - - unist-util-visit-parents@6.0.2: - resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} - - unist-util-visit@3.1.0: - resolution: {integrity: sha512-Szoh+R/Ll68QWAyQyZZpQzZQm2UPbxibDvaY8Xc9SUtYgPsDzx5AWSk++UUt2hJuow8mvwR+rG+LQLw+KsuAKA==} - - unist-util-visit@5.1.0: - resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} - - universal-user-agent@7.0.3: - resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} - - unraw@3.0.0: - resolution: {integrity: sha512-08/DA66UF65OlpUDIQtbJyrqTR0jTAlJ+jsnkQ4jxR7+K5g5YG1APZKQSMCE1vqqmD+2pv6+IdEjmopFatacvg==} - - url-parse@1.5.10: - resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} - - 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-stick-to-bottom@1.1.6: - resolution: {integrity: sha512-z3Up8jYQGTkUCsGBnwg6/wj70KgXoW5Kz1AAc1j8MtQuYMBo6ZsdhrIXoegxa7gaMMilgQYyTohTrt3p94jHog==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - 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 - - usehooks-ts@3.1.1: - resolution: {integrity: sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA==} - engines: {node: '>=16.15.0'} - peerDependencies: - react: ^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - uuid@11.1.1: - resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==} - hasBin: true - - 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==} - - victory-vendor@37.3.6: - resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} - - vite@7.3.5: - resolution: {integrity: sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - jiti: '>=1.21.0' - less: ^4.0.0 - lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - - vitest@4.1.8: - resolution: {integrity: sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.8 - '@vitest/browser-preview': 4.1.8 - '@vitest/browser-webdriverio': 4.1.8 - '@vitest/coverage-istanbul': 4.1.8 - '@vitest/coverage-v8': 4.1.8 - '@vitest/ui': 4.1.8 - happy-dom: '*' - jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@opentelemetry/api': - optional: true - '@types/node': - optional: true - '@vitest/browser-playwright': - optional: true - '@vitest/browser-preview': - optional: true - '@vitest/browser-webdriverio': - optional: true - '@vitest/coverage-istanbul': - optional: true - '@vitest/coverage-v8': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - - web-namespaces@2.0.1: - resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} - - web-tree-sitter@0.24.5: - resolution: {integrity: sha512-+J/2VSHN8J47gQUAvF8KDadrfz6uFYVjxoxbKWDoXVsH2u7yLdarCnIURnrMA6uSRkgX3SdmqM5BOoQjPdSh5w==} - - web-vitals@5.2.0: - resolution: {integrity: sha512-i2z98bEmaCqSDiHEDu+gHl/dmR4Q+TxFmG3/13KkMO+o8UxQzCqWaDRCiLgEa41nlO4VpXSI0ASa1xWmO9sBlA==} - - which-typed-array@1.1.19: - resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} - engines: {node: '>= 0.4'} - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} - hasBin: true - - wicked-good-xpath@1.3.0: - resolution: {integrity: sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==} - - wrap-ansi@9.0.2: - resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} - engines: {node: '>=18'} - - xml-but-prettier@1.0.1: - resolution: {integrity: sha512-C2CJaadHrZTqESlH03WOyw0oZTtoy2uEg6dSDF6YRg+9GnYNub53RRemLpnvtbHDFelxMx4LajiFsYeR6XJHgQ==} - - xml@1.0.1: - resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==} - - yaml@2.8.3: - resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} - engines: {node: '>= 14.6'} - hasBin: true - - yoctocolors@2.1.2: - resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} - engines: {node: '>=18'} - - zenscroll@4.0.2: - resolution: {integrity: sha512-jEA1znR7b4C/NnaycInCU6h/d15ZzCd1jmsruqOKnZP6WXQSMH3W2GL+OXbkruslU4h+Tzuos0HdswzRUk/Vgg==} - - zod@4.3.6: - resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} - - zustand@5.0.14: - resolution: {integrity: sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==} - engines: {node: '>=12.20.0'} - peerDependencies: - '@types/react': '>=18.0.0' - immer: '>=9.0.6' - react: '>=18.0.0' - use-sync-external-store: '>=1.2.0' - peerDependenciesMeta: - '@types/react': - optional: true - immer: - optional: true - react: - optional: true - use-sync-external-store: - optional: true - - zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - -snapshots: - - '@ai-sdk/gateway@4.0.28(zod@4.3.6)': - dependencies: - '@ai-sdk/provider': 4.0.3 - '@ai-sdk/provider-utils': 5.0.12(zod@4.3.6) - '@vercel/oidc': 3.2.0 - zod: 4.3.6 - - '@ai-sdk/mcp@2.0.16(zod@4.3.6)': - dependencies: - '@ai-sdk/provider': 4.0.3 - '@ai-sdk/provider-utils': 5.0.12(zod@4.3.6) - pkce-challenge: 5.0.1 - zod: 4.3.6 - - '@ai-sdk/provider-utils@5.0.12(zod@4.3.6)': - dependencies: - '@ai-sdk/provider': 4.0.3 - '@standard-schema/spec': 1.1.0 - '@workflow/serde': 4.1.0 - eventsource-parser: 3.1.0 - zod: 4.3.6 - - '@ai-sdk/provider@4.0.3': - dependencies: - json-schema: 0.4.0 - - '@ai-sdk/react@4.0.40(react@19.2.7)(zod@4.3.6)': - dependencies: - '@ai-sdk/mcp': 2.0.16(zod@4.3.6) - '@ai-sdk/provider': 4.0.3 - '@ai-sdk/provider-utils': 5.0.12(zod@4.3.6) - ai: 7.0.37(zod@4.3.6) - react: 19.2.7 - swr: 2.4.2(react@19.2.7) - throttleit: 2.1.0 - transitivePeerDependencies: - - zod - - '@algolia/abtesting@1.19.0': - dependencies: - '@algolia/client-common': 5.53.0 - '@algolia/requester-browser-xhr': 5.53.0 - '@algolia/requester-fetch': 5.53.0 - '@algolia/requester-node-http': 5.53.0 - - '@algolia/client-abtesting@5.53.0': - dependencies: - '@algolia/client-common': 5.53.0 - '@algolia/requester-browser-xhr': 5.53.0 - '@algolia/requester-fetch': 5.53.0 - '@algolia/requester-node-http': 5.53.0 - - '@algolia/client-analytics@5.53.0': - dependencies: - '@algolia/client-common': 5.53.0 - '@algolia/requester-browser-xhr': 5.53.0 - '@algolia/requester-fetch': 5.53.0 - '@algolia/requester-node-http': 5.53.0 - - '@algolia/client-common@5.53.0': {} - - '@algolia/client-insights@5.53.0': - dependencies: - '@algolia/client-common': 5.53.0 - '@algolia/requester-browser-xhr': 5.53.0 - '@algolia/requester-fetch': 5.53.0 - '@algolia/requester-node-http': 5.53.0 - - '@algolia/client-personalization@5.53.0': - dependencies: - '@algolia/client-common': 5.53.0 - '@algolia/requester-browser-xhr': 5.53.0 - '@algolia/requester-fetch': 5.53.0 - '@algolia/requester-node-http': 5.53.0 - - '@algolia/client-query-suggestions@5.53.0': - dependencies: - '@algolia/client-common': 5.53.0 - '@algolia/requester-browser-xhr': 5.53.0 - '@algolia/requester-fetch': 5.53.0 - '@algolia/requester-node-http': 5.53.0 - - '@algolia/client-search@5.53.0': - dependencies: - '@algolia/client-common': 5.53.0 - '@algolia/requester-browser-xhr': 5.53.0 - '@algolia/requester-fetch': 5.53.0 - '@algolia/requester-node-http': 5.53.0 - - '@algolia/events@4.0.1': {} - - '@algolia/ingestion@1.53.0': - dependencies: - '@algolia/client-common': 5.53.0 - '@algolia/requester-browser-xhr': 5.53.0 - '@algolia/requester-fetch': 5.53.0 - '@algolia/requester-node-http': 5.53.0 - - '@algolia/monitoring@1.53.0': - dependencies: - '@algolia/client-common': 5.53.0 - '@algolia/requester-browser-xhr': 5.53.0 - '@algolia/requester-fetch': 5.53.0 - '@algolia/requester-node-http': 5.53.0 - - '@algolia/recommend@5.53.0': - dependencies: - '@algolia/client-common': 5.53.0 - '@algolia/requester-browser-xhr': 5.53.0 - '@algolia/requester-fetch': 5.53.0 - '@algolia/requester-node-http': 5.53.0 - - '@algolia/requester-browser-xhr@5.53.0': - dependencies: - '@algolia/client-common': 5.53.0 - - '@algolia/requester-fetch@5.53.0': - dependencies: - '@algolia/client-common': 5.53.0 - - '@algolia/requester-node-http@5.53.0': - dependencies: - '@algolia/client-common': 5.53.0 - - '@alloc/quick-lru@5.2.0': {} - - '@antfu/install-pkg@1.1.0': - dependencies: - package-manager-detector: 1.6.0 - tinyexec: 1.1.1 - - '@anthropic-ai/sdk@0.91.1(zod@4.3.6)': - dependencies: - json-schema-to-ts: 3.1.1 - optionalDependencies: - zod: 4.3.6 - - '@arcadeai/arcadejs@2.4.1': {} - - '@arcadeai/design-system@7.11.0(@date-fns/tz@1.4.1)(@hookform/resolvers@5.2.2(react-hook-form@7.77.0(react@19.2.7)))(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(lucide-react@0.577.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react-hook-form@7.77.0(react@19.2.7))(react@19.2.7)(recharts@3.6.0(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react-is@16.13.1)(react@19.2.7)(redux@5.0.1))(tailwindcss@4.3.0)': - dependencies: - '@base-ui/react': 1.6.0(@date-fns/tz@1.4.1)(@types/react@19.2.16)(date-fns@4.4.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@base-ui/utils': 0.3.1(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@fontsource-variable/geist': 5.2.9 - '@hookform/resolvers': 5.2.2(react-hook-form@7.77.0(react@19.2.7)) - '@streamdown/code': 1.1.1(react@19.2.7) - class-variance-authority: 0.7.1 - clsx: 2.1.1 - cmdk: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - date-fns: 4.4.0 - lucide-react: 0.577.0(react@19.2.7) - react: 19.2.7 - react-day-picker: 10.0.1(@types/react@19.2.16)(react@19.2.7) - react-dom: 19.2.7(react@19.2.7) - react-hook-form: 7.77.0(react@19.2.7) - react-resizable-panels: 4.11.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react-shiki: 0.10.1(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - recharts: 3.6.0(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react-is@16.13.1)(react@19.2.7)(redux@5.0.1) - remark-breaks: 4.0.0 - remark-gfm: 4.0.1 - streamdown: 2.5.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - tailwind-merge: 3.6.0 - tailwindcss: 4.3.0 - use-stick-to-bottom: 1.1.6(react@19.2.7) - transitivePeerDependencies: - - '@date-fns/tz' - - '@types/react' - - '@types/react-dom' - - supports-color - - '@arcadeai/ui-kit@0.14.0(@ai-sdk/react@4.0.40(react@19.2.7)(zod@4.3.6))(@arcadeai/design-system@7.11.0(@date-fns/tz@1.4.1)(@hookform/resolvers@5.2.2(react-hook-form@7.77.0(react@19.2.7)))(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(lucide-react@0.577.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react-hook-form@7.77.0(react@19.2.7))(react@19.2.7)(recharts@3.6.0(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react-is@16.13.1)(react@19.2.7)(redux@5.0.1))(tailwindcss@4.3.0))(@tanstack/react-query@5.101.4(react@19.2.7))(@tanstack/react-router@1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(ai@7.0.37(zod@4.3.6))(lucide-react@0.577.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(tailwindcss@4.3.0)': - dependencies: - '@ai-sdk/react': 4.0.40(react@19.2.7)(zod@4.3.6) - '@arcadeai/arcadejs': 2.4.1 - '@arcadeai/design-system': 7.11.0(@date-fns/tz@1.4.1)(@hookform/resolvers@5.2.2(react-hook-form@7.77.0(react@19.2.7)))(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(lucide-react@0.577.0(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react-hook-form@7.77.0(react@19.2.7))(react@19.2.7)(recharts@3.6.0(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react-is@16.13.1)(react@19.2.7)(redux@5.0.1))(tailwindcss@4.3.0) - '@tanstack/react-query': 5.101.4(react@19.2.7) - '@tanstack/react-router': 1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/react-virtual': 3.14.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - ai: 7.0.37(zod@4.3.6) - lucide-react: 0.577.0(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - tailwindcss: 4.3.0 - usehooks-ts: 3.1.1(react@19.2.7) - - '@babel/runtime-corejs3@7.28.4': - dependencies: - core-js-pure: 3.47.0 - - '@babel/runtime@7.28.4': {} - - '@babel/runtime@7.29.7': {} - - '@base-ui/react@1.6.0(@date-fns/tz@1.4.1)(@types/react@19.2.16)(date-fns@4.4.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@babel/runtime': 7.29.7 - '@base-ui/utils': 0.3.1(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@floating-ui/utils': 0.2.11 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - use-sync-external-store: 1.6.0(react@19.2.7) - optionalDependencies: - '@date-fns/tz': 1.4.1 - '@types/react': 19.2.16 - date-fns: 4.4.0 - - '@base-ui/utils@0.3.1(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@babel/runtime': 7.29.7 - '@floating-ui/utils': 0.2.11 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - reselect: 5.2.0 - use-sync-external-store: 1.6.0(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.16 - - '@biomejs/biome@2.3.2': - optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.3.2 - '@biomejs/cli-darwin-x64': 2.3.2 - '@biomejs/cli-linux-arm64': 2.3.2 - '@biomejs/cli-linux-arm64-musl': 2.3.2 - '@biomejs/cli-linux-x64': 2.3.2 - '@biomejs/cli-linux-x64-musl': 2.3.2 - '@biomejs/cli-win32-arm64': 2.3.2 - '@biomejs/cli-win32-x64': 2.3.2 - - '@biomejs/cli-darwin-arm64@2.3.2': - optional: true - - '@biomejs/cli-darwin-x64@2.3.2': - optional: true - - '@biomejs/cli-linux-arm64-musl@2.3.2': - optional: true - - '@biomejs/cli-linux-arm64@2.3.2': - optional: true - - '@biomejs/cli-linux-x64-musl@2.3.2': - optional: true - - '@biomejs/cli-linux-x64@2.3.2': - optional: true - - '@biomejs/cli-win32-arm64@2.3.2': - optional: true - - '@biomejs/cli-win32-x64@2.3.2': - optional: true - - '@braintree/sanitize-url@7.1.2': {} - - '@chevrotain/types@11.1.2': {} - - '@clack/core@0.5.0': - dependencies: - picocolors: 1.1.1 - sisteransi: 1.0.5 - - '@clack/prompts@0.11.0': - dependencies: - '@clack/core': 0.5.0 - picocolors: 1.1.1 - sisteransi: 1.0.5 - - '@date-fns/tz@1.4.1': {} - - '@emnapi/runtime@1.11.3': - dependencies: - tslib: 2.8.1 - optional: true - - '@esbuild/aix-ppc64@0.28.1': - optional: true - - '@esbuild/android-arm64@0.28.1': - optional: true - - '@esbuild/android-arm@0.28.1': - optional: true - - '@esbuild/android-x64@0.28.1': - optional: true - - '@esbuild/darwin-arm64@0.28.1': - optional: true - - '@esbuild/darwin-x64@0.28.1': - optional: true - - '@esbuild/freebsd-arm64@0.28.1': - optional: true - - '@esbuild/freebsd-x64@0.28.1': - optional: true - - '@esbuild/linux-arm64@0.28.1': - optional: true - - '@esbuild/linux-arm@0.28.1': - optional: true - - '@esbuild/linux-ia32@0.28.1': - optional: true - - '@esbuild/linux-loong64@0.28.1': - optional: true - - '@esbuild/linux-mips64el@0.28.1': - optional: true - - '@esbuild/linux-ppc64@0.28.1': - optional: true - - '@esbuild/linux-riscv64@0.28.1': - optional: true - - '@esbuild/linux-s390x@0.28.1': - optional: true - - '@esbuild/linux-x64@0.28.1': - optional: true - - '@esbuild/netbsd-arm64@0.28.1': - optional: true - - '@esbuild/netbsd-x64@0.28.1': - optional: true - - '@esbuild/openbsd-arm64@0.28.1': - optional: true - - '@esbuild/openbsd-x64@0.28.1': - optional: true - - '@esbuild/openharmony-arm64@0.28.1': - optional: true - - '@esbuild/sunos-x64@0.28.1': - optional: true - - '@esbuild/win32-arm64@0.28.1': - optional: true - - '@esbuild/win32-ia32@0.28.1': - optional: true - - '@esbuild/win32-x64@0.28.1': - optional: true - - '@floating-ui/core@1.7.5': - dependencies: - '@floating-ui/utils': 0.2.11 - - '@floating-ui/dom@1.7.6': - dependencies: - '@floating-ui/core': 1.7.5 - '@floating-ui/utils': 0.2.11 - - '@floating-ui/react-dom@2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@floating-ui/dom': 1.7.6 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - - '@floating-ui/react@0.26.28(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@floating-ui/utils': 0.2.11 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - tabbable: 6.3.0 - - '@floating-ui/utils@0.2.11': {} - - '@fontsource-variable/geist@5.2.9': {} - - '@formatjs/intl-localematcher@0.6.2': - dependencies: - tslib: 2.8.1 - - '@headlessui/react@2.2.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@floating-ui/react': 0.26.28(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@react-aria/focus': 3.21.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@react-aria/interactions': 3.25.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/react-virtual': 3.14.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - use-sync-external-store: 1.6.0(react@19.2.7) - - '@hookform/resolvers@5.2.2(react-hook-form@7.77.0(react@19.2.7))': - dependencies: - '@standard-schema/utils': 0.3.0 - react-hook-form: 7.77.0(react@19.2.7) - - '@iconify/types@2.0.0': {} - - '@iconify/utils@3.1.0': - dependencies: - '@antfu/install-pkg': 1.1.0 - '@iconify/types': 2.0.0 - mlly: 1.8.0 - - '@img/colour@1.1.0': - optional: true - - '@img/sharp-darwin-arm64@0.35.5': - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.3.4 - optional: true - - '@img/sharp-darwin-x64@0.35.5': - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.3.4 - optional: true - - '@img/sharp-freebsd-wasm32@0.35.5': - dependencies: - '@img/sharp-wasm32': 0.35.5 - optional: true - - '@img/sharp-libvips-darwin-arm64@1.3.4': - optional: true - - '@img/sharp-libvips-darwin-x64@1.3.4': - optional: true - - '@img/sharp-libvips-linux-arm64@1.3.4': - optional: true - - '@img/sharp-libvips-linux-arm@1.3.4': - optional: true - - '@img/sharp-libvips-linux-ppc64@1.3.4': - optional: true - - '@img/sharp-libvips-linux-riscv64@1.3.4': - optional: true - - '@img/sharp-libvips-linux-s390x@1.3.4': - optional: true - - '@img/sharp-libvips-linux-x64@1.3.4': - optional: true - - '@img/sharp-libvips-linuxmusl-arm64@1.3.4': - optional: true - - '@img/sharp-libvips-linuxmusl-x64@1.3.4': - optional: true - - '@img/sharp-linux-arm64@0.35.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.3.4 - optional: true - - '@img/sharp-linux-arm@0.35.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.3.4 - optional: true - - '@img/sharp-linux-ppc64@0.35.5': - optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.3.4 - optional: true - - '@img/sharp-linux-riscv64@0.35.5': - optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.3.4 - optional: true - - '@img/sharp-linux-s390x@0.35.5': - optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.3.4 - optional: true - - '@img/sharp-linux-x64@0.35.5': - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.3.4 - optional: true - - '@img/sharp-linuxmusl-arm64@0.35.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 - optional: true - - '@img/sharp-linuxmusl-x64@0.35.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.3.4 - optional: true - - '@img/sharp-wasm32@0.35.5': - dependencies: - '@emnapi/runtime': 1.11.3 - optional: true - - '@img/sharp-webcontainers-wasm32@0.35.5': - dependencies: - '@img/sharp-wasm32': 0.35.5 - optional: true - - '@img/sharp-win32-arm64@0.35.5': - optional: true - - '@img/sharp-win32-ia32@0.35.5': - optional: true - - '@img/sharp-win32-x64@0.35.5': - optional: true - - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@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.5.5': {} - - '@jridgewell/trace-mapping@0.3.31': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - - '@mdx-js/mdx@3.1.1': - dependencies: - '@types/estree': 1.0.8 - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.5 - '@types/mdx': 2.0.13 - acorn: 8.15.0 - collapse-white-space: 2.1.0 - devlop: 1.1.0 - estree-util-is-identifier-name: 3.0.0 - estree-util-scope: 1.0.0 - 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.15.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 - - '@mermaid-js/parser@1.2.1': - dependencies: - '@chevrotain/types': 11.1.2 - - '@napi-rs/simple-git-android-arm-eabi@0.1.22': - optional: true - - '@napi-rs/simple-git-android-arm64@0.1.22': - optional: true - - '@napi-rs/simple-git-darwin-arm64@0.1.22': - optional: true - - '@napi-rs/simple-git-darwin-x64@0.1.22': - optional: true - - '@napi-rs/simple-git-freebsd-x64@0.1.22': - optional: true - - '@napi-rs/simple-git-linux-arm-gnueabihf@0.1.22': - optional: true - - '@napi-rs/simple-git-linux-arm64-gnu@0.1.22': - optional: true - - '@napi-rs/simple-git-linux-arm64-musl@0.1.22': - optional: true - - '@napi-rs/simple-git-linux-ppc64-gnu@0.1.22': - optional: true - - '@napi-rs/simple-git-linux-s390x-gnu@0.1.22': - optional: true - - '@napi-rs/simple-git-linux-x64-gnu@0.1.22': - optional: true - - '@napi-rs/simple-git-linux-x64-musl@0.1.22': - optional: true - - '@napi-rs/simple-git-win32-arm64-msvc@0.1.22': - optional: true - - '@napi-rs/simple-git-win32-ia32-msvc@0.1.22': - optional: true - - '@napi-rs/simple-git-win32-x64-msvc@0.1.22': - optional: true - - '@napi-rs/simple-git@0.1.22': - optionalDependencies: - '@napi-rs/simple-git-android-arm-eabi': 0.1.22 - '@napi-rs/simple-git-android-arm64': 0.1.22 - '@napi-rs/simple-git-darwin-arm64': 0.1.22 - '@napi-rs/simple-git-darwin-x64': 0.1.22 - '@napi-rs/simple-git-freebsd-x64': 0.1.22 - '@napi-rs/simple-git-linux-arm-gnueabihf': 0.1.22 - '@napi-rs/simple-git-linux-arm64-gnu': 0.1.22 - '@napi-rs/simple-git-linux-arm64-musl': 0.1.22 - '@napi-rs/simple-git-linux-ppc64-gnu': 0.1.22 - '@napi-rs/simple-git-linux-s390x-gnu': 0.1.22 - '@napi-rs/simple-git-linux-x64-gnu': 0.1.22 - '@napi-rs/simple-git-linux-x64-musl': 0.1.22 - '@napi-rs/simple-git-win32-arm64-msvc': 0.1.22 - '@napi-rs/simple-git-win32-ia32-msvc': 0.1.22 - '@napi-rs/simple-git-win32-x64-msvc': 0.1.22 - - '@next/env@16.3.8': {} - - '@next/swc-darwin-arm64@16.3.8': - optional: true - - '@next/swc-darwin-x64@16.3.8': - optional: true - - '@next/swc-linux-arm64-gnu@16.3.8': - optional: true - - '@next/swc-linux-arm64-musl@16.3.8': - optional: true - - '@next/swc-linux-x64-gnu@16.3.8': - optional: true - - '@next/swc-linux-x64-musl@16.3.8': - optional: true - - '@next/swc-win32-arm64-msvc@16.3.8': - optional: true - - '@next/swc-win32-x64-msvc@16.3.8': - optional: true - - '@next/third-parties@16.3.8(next@16.3.8(@opentelemetry/api@1.9.0)(@types/node@22.19.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)': - dependencies: - next: 16.3.8(@opentelemetry/api@1.9.0)(@types/node@22.19.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: 19.2.7 - third-party-capital: 1.0.20 - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 - - '@octokit/auth-token@6.0.0': {} - - '@octokit/core@7.0.6': - dependencies: - '@octokit/auth-token': 6.0.0 - '@octokit/graphql': 9.0.3 - '@octokit/request': 10.0.7 - '@octokit/request-error': 7.1.0 - '@octokit/types': 16.0.0 - before-after-hook: 4.0.0 - universal-user-agent: 7.0.3 - - '@octokit/endpoint@11.0.2': - dependencies: - '@octokit/types': 16.0.0 - universal-user-agent: 7.0.3 - - '@octokit/graphql@9.0.3': - dependencies: - '@octokit/request': 10.0.7 - '@octokit/types': 16.0.0 - universal-user-agent: 7.0.3 - - '@octokit/openapi-types@27.0.0': {} - - '@octokit/plugin-paginate-rest@14.0.0(@octokit/core@7.0.6)': - dependencies: - '@octokit/core': 7.0.6 - '@octokit/types': 16.0.0 - - '@octokit/plugin-request-log@6.0.0(@octokit/core@7.0.6)': - dependencies: - '@octokit/core': 7.0.6 - - '@octokit/plugin-rest-endpoint-methods@17.0.0(@octokit/core@7.0.6)': - dependencies: - '@octokit/core': 7.0.6 - '@octokit/types': 16.0.0 - - '@octokit/request-error@7.1.0': - dependencies: - '@octokit/types': 16.0.0 - - '@octokit/request@10.0.7': - dependencies: - '@octokit/endpoint': 11.0.2 - '@octokit/request-error': 7.1.0 - '@octokit/types': 16.0.0 - fast-content-type-parse: 3.0.0 - universal-user-agent: 7.0.3 - - '@octokit/rest@22.0.1': - dependencies: - '@octokit/core': 7.0.6 - '@octokit/plugin-paginate-rest': 14.0.0(@octokit/core@7.0.6) - '@octokit/plugin-request-log': 6.0.0(@octokit/core@7.0.6) - '@octokit/plugin-rest-endpoint-methods': 17.0.0(@octokit/core@7.0.6) - - '@octokit/types@16.0.0': - dependencies: - '@octokit/openapi-types': 27.0.0 - - '@opentelemetry/api-logs@0.208.0': - dependencies: - '@opentelemetry/api': 1.9.0 - - '@opentelemetry/api@1.9.0': {} - - '@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.39.0 - - '@opentelemetry/exporter-logs-otlp-http@0.208.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.208.0 - '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-exporter-base': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) - - '@opentelemetry/otlp-exporter-base@0.208.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.0) - '@opentelemetry/otlp-transformer': 0.208.0(@opentelemetry/api@1.9.0) - - '@opentelemetry/otlp-transformer@0.208.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.208.0 - '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-metrics': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) - protobufjs: 7.6.5 - - '@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - - '@opentelemetry/resources@2.5.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - - '@opentelemetry/sdk-logs@0.208.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.208.0 - '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - - '@opentelemetry/sdk-metrics@2.2.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - - '@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - - '@opentelemetry/semantic-conventions@1.39.0': {} - - '@ory/client@1.22.37': - dependencies: - axios: 1.20.0 - transitivePeerDependencies: - - debug - - supports-color - - '@posthog/core@1.30.3': - dependencies: - '@posthog/types': 1.379.0 - - '@posthog/types@1.379.0': {} - - '@protobufjs/aspromise@1.1.2': {} - - '@protobufjs/base64@1.1.2': {} - - '@protobufjs/codegen@2.0.5': {} - - '@protobufjs/eventemitter@1.1.1': {} - - '@protobufjs/fetch@1.1.1': - dependencies: - '@protobufjs/aspromise': 1.1.2 - - '@protobufjs/float@1.0.2': {} - - '@protobufjs/path@1.1.2': {} - - '@protobufjs/pool@1.1.0': {} - - '@protobufjs/utf8@1.1.2': {} - - '@radix-ui/primitive@1.1.3': {} - - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.16)(react@19.2.7)': - dependencies: - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.16 - - '@radix-ui/react-context@1.1.2(@types/react@19.2.16)(react@19.2.7)': - dependencies: - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.16 - - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.16)(react@19.2.7) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.16)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.16)(react@19.2.7) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.16)(react@19.2.7) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.16)(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.16)(react@19.2.7) - aria-hidden: 1.2.6 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - react-remove-scroll: 2.7.2(@types/react@19.2.16)(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.16 - '@types/react-dom': 19.2.3(@types/react@19.2.16) - - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.16)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.16)(react@19.2.7) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.16)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.16 - '@types/react-dom': 19.2.3(@types/react@19.2.16) - - '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.16)(react@19.2.7)': - dependencies: - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.16 - - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.16)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.16)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.16 - '@types/react-dom': 19.2.3(@types/react@19.2.16) - - '@radix-ui/react-id@1.1.1(@types/react@19.2.16)(react@19.2.7)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.16)(react@19.2.7) - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.16 - - '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.16)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.16 - '@types/react-dom': 19.2.3(@types/react@19.2.16) - - '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.16)(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.16)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.16 - '@types/react-dom': 19.2.3(@types/react@19.2.16) - - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.16)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.16 - '@types/react-dom': 19.2.3(@types/react@19.2.16) - - '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/react-slot': 1.2.4(@types/react@19.2.16)(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.16 - '@types/react-dom': 19.2.3(@types/react@19.2.16) - - '@radix-ui/react-slot@1.2.3(@types/react@19.2.16)(react@19.2.7)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.16)(react@19.2.7) - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.16 - - '@radix-ui/react-slot@1.2.4(@types/react@19.2.16)(react@19.2.7)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.16)(react@19.2.7) - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.16 - - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.16)(react@19.2.7)': - dependencies: - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.16 - - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.16)(react@19.2.7)': - dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.16)(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.16)(react@19.2.7) - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.16 - - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.16)(react@19.2.7)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.16)(react@19.2.7) - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.16 - - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.16)(react@19.2.7)': - dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.16)(react@19.2.7) - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.16 - - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.16)(react@19.2.7)': - dependencies: - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.16 - - '@react-aria/focus@3.21.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@react-aria/interactions': 3.25.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@react-aria/utils': 3.31.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@react-types/shared': 3.32.1(react@19.2.7) - '@swc/helpers': 0.5.23 - clsx: 2.1.1 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - - '@react-aria/interactions@3.25.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@react-aria/ssr': 3.9.10(react@19.2.7) - '@react-aria/utils': 3.31.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@react-stately/flags': 3.1.2 - '@react-types/shared': 3.32.1(react@19.2.7) - '@swc/helpers': 0.5.23 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - - '@react-aria/ssr@3.9.10(react@19.2.7)': - dependencies: - '@swc/helpers': 0.5.23 - react: 19.2.7 - - '@react-aria/utils@3.31.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@react-aria/ssr': 3.9.10(react@19.2.7) - '@react-stately/flags': 3.1.2 - '@react-stately/utils': 3.10.8(react@19.2.7) - '@react-types/shared': 3.32.1(react@19.2.7) - '@swc/helpers': 0.5.23 - clsx: 2.1.1 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - - '@react-stately/flags@3.1.2': - dependencies: - '@swc/helpers': 0.5.23 - - '@react-stately/utils@3.10.8(react@19.2.7)': - dependencies: - '@swc/helpers': 0.5.23 - react: 19.2.7 - - '@react-types/shared@3.32.1(react@19.2.7)': - dependencies: - react: 19.2.7 - - '@reduxjs/toolkit@2.12.0(react-redux@9.3.0(@types/react@19.2.16)(react@19.2.7)(redux@5.0.1))(react@19.2.7)': - dependencies: - '@standard-schema/spec': 1.1.0 - '@standard-schema/utils': 0.3.0 - immer: 11.1.15 - redux: 5.0.1 - redux-thunk: 3.1.0(redux@5.0.1) - reselect: 5.2.0 - optionalDependencies: - react: 19.2.7 - react-redux: 9.3.0(@types/react@19.2.16)(react@19.2.7)(redux@5.0.1) - - '@rollup/rollup-android-arm-eabi@4.62.2': - optional: true - - '@rollup/rollup-android-arm64@4.62.2': - optional: true - - '@rollup/rollup-darwin-arm64@4.62.2': - optional: true - - '@rollup/rollup-darwin-x64@4.62.2': - optional: true - - '@rollup/rollup-freebsd-arm64@4.62.2': - optional: true - - '@rollup/rollup-freebsd-x64@4.62.2': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.62.2': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.62.2': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.62.2': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.62.2': - optional: true - - '@rollup/rollup-linux-loong64-gnu@4.62.2': - optional: true - - '@rollup/rollup-linux-loong64-musl@4.62.2': - optional: true - - '@rollup/rollup-linux-ppc64-gnu@4.62.2': - optional: true - - '@rollup/rollup-linux-ppc64-musl@4.62.2': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.62.2': - optional: true - - '@rollup/rollup-linux-riscv64-musl@4.62.2': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.62.2': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.62.2': - optional: true - - '@rollup/rollup-linux-x64-musl@4.62.2': - optional: true - - '@rollup/rollup-openbsd-x64@4.62.2': - optional: true - - '@rollup/rollup-openharmony-arm64@4.62.2': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.62.2': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.62.2': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.62.2': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.62.2': - optional: true - - '@scarf/scarf@1.4.0': {} - - '@shikijs/core@3.20.0': - dependencies: - '@shikijs/types': 3.20.0 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.5 - hast-util-to-html: 9.0.5 - - '@shikijs/core@4.1.0': - dependencies: - '@shikijs/primitive': 4.1.0 - '@shikijs/types': 4.1.0 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.5 - hast-util-to-html: 9.0.5 - - '@shikijs/engine-javascript@3.20.0': - dependencies: - '@shikijs/types': 3.20.0 - '@shikijs/vscode-textmate': 10.0.2 - oniguruma-to-es: 4.3.6 - - '@shikijs/engine-javascript@4.1.0': - dependencies: - '@shikijs/types': 4.1.0 - '@shikijs/vscode-textmate': 10.0.2 - oniguruma-to-es: 4.3.6 - - '@shikijs/engine-oniguruma@3.20.0': - dependencies: - '@shikijs/types': 3.20.0 - '@shikijs/vscode-textmate': 10.0.2 - - '@shikijs/engine-oniguruma@4.1.0': - dependencies: - '@shikijs/types': 4.1.0 - '@shikijs/vscode-textmate': 10.0.2 - - '@shikijs/langs@3.20.0': - dependencies: - '@shikijs/types': 3.20.0 - - '@shikijs/langs@4.1.0': - dependencies: - '@shikijs/types': 4.1.0 - - '@shikijs/primitive@4.1.0': - dependencies: - '@shikijs/types': 4.1.0 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.5 - - '@shikijs/themes@3.20.0': - dependencies: - '@shikijs/types': 3.20.0 - - '@shikijs/themes@4.1.0': - dependencies: - '@shikijs/types': 4.1.0 - - '@shikijs/twoslash@3.20.0(typescript@5.9.3)': - dependencies: - '@shikijs/core': 3.20.0 - '@shikijs/types': 3.20.0 - twoslash: 0.3.4(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@shikijs/types@3.20.0': - dependencies: - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.5 - - '@shikijs/types@4.1.0': - dependencies: - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.5 - - '@shikijs/vscode-textmate@10.0.2': {} - - '@standard-schema/spec@1.1.0': {} - - '@standard-schema/utils@0.3.0': {} - - '@streamdown/code@1.1.1(react@19.2.7)': - dependencies: - react: 19.2.7 - shiki: 3.20.0 - - '@swagger-api/apidom-ast@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-error': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - unraw: 3.0.0 - - '@swagger-api/apidom-core@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-ast': 1.12.0 - '@swagger-api/apidom-error': 1.12.0 - '@types/ramda': 0.30.2 - minim: 0.23.8 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - short-unique-id: 5.3.2 - ts-mixer: 6.0.4 - - '@swagger-api/apidom-error@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - - '@swagger-api/apidom-json-pointer@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-error': 1.12.0 - '@swaggerexpert/json-pointer': 2.10.2 - - '@swagger-api/apidom-ns-a2a-1@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - ts-mixer: 6.0.4 - optional: true - - '@swagger-api/apidom-ns-api-design-systems@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-error': 1.12.0 - '@swagger-api/apidom-ns-openapi-3-1': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - ts-mixer: 6.0.4 - optional: true - - '@swagger-api/apidom-ns-arazzo-1@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-json-schema-2020-12': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - ts-mixer: 6.0.4 - optional: true - - '@swagger-api/apidom-ns-asyncapi-2@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-json-schema-draft-7': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - ts-mixer: 6.0.4 - optional: true - - '@swagger-api/apidom-ns-asyncapi-3@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-asyncapi-2': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - ts-mixer: 6.0.4 - optional: true - - '@swagger-api/apidom-ns-json-schema-2019-09@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-error': 1.12.0 - '@swagger-api/apidom-ns-json-schema-draft-7': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - ts-mixer: 6.0.4 - - '@swagger-api/apidom-ns-json-schema-2020-12@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-error': 1.12.0 - '@swagger-api/apidom-ns-json-schema-2019-09': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - ts-mixer: 6.0.4 - - '@swagger-api/apidom-ns-json-schema-draft-4@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-ast': 1.12.0 - '@swagger-api/apidom-core': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - ts-mixer: 6.0.4 - - '@swagger-api/apidom-ns-json-schema-draft-6@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-error': 1.12.0 - '@swagger-api/apidom-ns-json-schema-draft-4': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - ts-mixer: 6.0.4 - - '@swagger-api/apidom-ns-json-schema-draft-7@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-error': 1.12.0 - '@swagger-api/apidom-ns-json-schema-draft-6': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - ts-mixer: 6.0.4 - - '@swagger-api/apidom-ns-openapi-2@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-error': 1.12.0 - '@swagger-api/apidom-ns-json-schema-draft-4': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - ts-mixer: 6.0.4 - optional: true - - '@swagger-api/apidom-ns-openapi-3-0@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-error': 1.12.0 - '@swagger-api/apidom-ns-json-schema-draft-4': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - ts-mixer: 6.0.4 - - '@swagger-api/apidom-ns-openapi-3-1@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-ast': 1.12.0 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-json-pointer': 1.12.0 - '@swagger-api/apidom-ns-json-schema-2020-12': 1.12.0 - '@swagger-api/apidom-ns-openapi-3-0': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - ts-mixer: 6.0.4 - - '@swagger-api/apidom-ns-openapi-3-2@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-ast': 1.12.0 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-json-pointer': 1.12.0 - '@swagger-api/apidom-ns-json-schema-2020-12': 1.12.0 - '@swagger-api/apidom-ns-openapi-3-0': 1.12.0 - '@swagger-api/apidom-ns-openapi-3-1': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - ts-mixer: 6.0.4 - - '@swagger-api/apidom-parser-adapter-a2a-json-1@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-a2a-1': 1.12.0 - '@swagger-api/apidom-parser-adapter-json': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optional: true - - '@swagger-api/apidom-parser-adapter-a2a-yaml-1@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-a2a-1': 1.12.0 - '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optional: true - - '@swagger-api/apidom-parser-adapter-api-design-systems-json@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-api-design-systems': 1.12.0 - '@swagger-api/apidom-parser-adapter-json': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optional: true - - '@swagger-api/apidom-parser-adapter-api-design-systems-yaml@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-api-design-systems': 1.12.0 - '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optional: true - - '@swagger-api/apidom-parser-adapter-arazzo-json-1@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-arazzo-1': 1.12.0 - '@swagger-api/apidom-parser-adapter-json': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optional: true - - '@swagger-api/apidom-parser-adapter-arazzo-yaml-1@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-arazzo-1': 1.12.0 - '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optional: true - - '@swagger-api/apidom-parser-adapter-asyncapi-json-2@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-asyncapi-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-json': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optional: true - - '@swagger-api/apidom-parser-adapter-asyncapi-json-3@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-asyncapi-3': 1.12.0 - '@swagger-api/apidom-parser-adapter-json': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optional: true - - '@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-asyncapi-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optional: true - - '@swagger-api/apidom-parser-adapter-asyncapi-yaml-3@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-asyncapi-3': 1.12.0 - '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optional: true - - '@swagger-api/apidom-parser-adapter-json@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-ast': 1.12.0 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-error': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - tree-sitter: 0.21.1 - tree-sitter-json: 0.24.8(tree-sitter@0.21.1) - web-tree-sitter: 0.24.5 - optional: true - - '@swagger-api/apidom-parser-adapter-openapi-json-2@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-openapi-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-json': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optional: true - - '@swagger-api/apidom-parser-adapter-openapi-json-3-0@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-openapi-3-0': 1.12.0 - '@swagger-api/apidom-parser-adapter-json': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optional: true - - '@swagger-api/apidom-parser-adapter-openapi-json-3-1@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-openapi-3-1': 1.12.0 - '@swagger-api/apidom-parser-adapter-json': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optional: true - - '@swagger-api/apidom-parser-adapter-openapi-json-3-2@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-openapi-3-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-json': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optional: true - - '@swagger-api/apidom-parser-adapter-openapi-yaml-2@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-openapi-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optional: true - - '@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-openapi-3-0': 1.12.0 - '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optional: true - - '@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-openapi-3-1': 1.12.0 - '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optional: true - - '@swagger-api/apidom-parser-adapter-openapi-yaml-3-2@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-ns-openapi-3-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.12.0 - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optional: true - - '@swagger-api/apidom-parser-adapter-yaml-1-2@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-ast': 1.12.0 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-error': 1.12.0 - '@tree-sitter-grammars/tree-sitter-yaml': 0.7.1(tree-sitter@0.22.4) - '@types/ramda': 0.30.2 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - tree-sitter: 0.22.4 - web-tree-sitter: 0.24.5 - optional: true - - '@swagger-api/apidom-reference@1.12.0': - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-error': 1.12.0 - '@types/ramda': 0.30.2 - axios: 1.20.0 - minimatch: 10.2.6 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optionalDependencies: - '@swagger-api/apidom-json-pointer': 1.12.0 - '@swagger-api/apidom-ns-a2a-1': 1.12.0 - '@swagger-api/apidom-ns-arazzo-1': 1.12.0 - '@swagger-api/apidom-ns-asyncapi-2': 1.12.0 - '@swagger-api/apidom-ns-openapi-2': 1.12.0 - '@swagger-api/apidom-ns-openapi-3-0': 1.12.0 - '@swagger-api/apidom-ns-openapi-3-1': 1.12.0 - '@swagger-api/apidom-ns-openapi-3-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-a2a-json-1': 1.12.0 - '@swagger-api/apidom-parser-adapter-a2a-yaml-1': 1.12.0 - '@swagger-api/apidom-parser-adapter-api-design-systems-json': 1.12.0 - '@swagger-api/apidom-parser-adapter-api-design-systems-yaml': 1.12.0 - '@swagger-api/apidom-parser-adapter-arazzo-json-1': 1.12.0 - '@swagger-api/apidom-parser-adapter-arazzo-yaml-1': 1.12.0 - '@swagger-api/apidom-parser-adapter-asyncapi-json-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-asyncapi-json-3': 1.12.0 - '@swagger-api/apidom-parser-adapter-asyncapi-yaml-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-asyncapi-yaml-3': 1.12.0 - '@swagger-api/apidom-parser-adapter-json': 1.12.0 - '@swagger-api/apidom-parser-adapter-openapi-json-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-openapi-json-3-0': 1.12.0 - '@swagger-api/apidom-parser-adapter-openapi-json-3-1': 1.12.0 - '@swagger-api/apidom-parser-adapter-openapi-json-3-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-openapi-yaml-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-openapi-yaml-3-0': 1.12.0 - '@swagger-api/apidom-parser-adapter-openapi-yaml-3-1': 1.12.0 - '@swagger-api/apidom-parser-adapter-openapi-yaml-3-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.12.0 - transitivePeerDependencies: - - debug - - supports-color - - '@swaggerexpert/cookie@2.0.2': - dependencies: - apg-lite: 1.0.5 - - '@swaggerexpert/json-pointer@2.10.2': - dependencies: - apg-lite: 1.0.5 - - '@swc/helpers@0.5.18': - dependencies: - tslib: 2.8.1 - - '@swc/helpers@0.5.23': - dependencies: - tslib: 2.8.1 - - '@tailwindcss/node@4.3.0': - dependencies: - '@jridgewell/remapping': 2.3.5 - enhanced-resolve: 5.21.0 - jiti: 2.6.1 - lightningcss: 1.32.0 - magic-string: 0.30.21 - source-map-js: 1.2.2 - tailwindcss: 4.3.0 - - '@tailwindcss/oxide-android-arm64@4.3.0': - optional: true - - '@tailwindcss/oxide-darwin-arm64@4.3.0': - optional: true - - '@tailwindcss/oxide-darwin-x64@4.3.0': - optional: true - - '@tailwindcss/oxide-freebsd-x64@4.3.0': - optional: true - - '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0': - optional: true - - '@tailwindcss/oxide-linux-arm64-gnu@4.3.0': - optional: true - - '@tailwindcss/oxide-linux-arm64-musl@4.3.0': - optional: true - - '@tailwindcss/oxide-linux-x64-gnu@4.3.0': - optional: true - - '@tailwindcss/oxide-linux-x64-musl@4.3.0': - optional: true - - '@tailwindcss/oxide-wasm32-wasi@4.3.0': - optional: true - - '@tailwindcss/oxide-win32-arm64-msvc@4.3.0': - optional: true - - '@tailwindcss/oxide-win32-x64-msvc@4.3.0': - optional: true - - '@tailwindcss/oxide@4.3.0': - optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.3.0 - '@tailwindcss/oxide-darwin-arm64': 4.3.0 - '@tailwindcss/oxide-darwin-x64': 4.3.0 - '@tailwindcss/oxide-freebsd-x64': 4.3.0 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.0 - '@tailwindcss/oxide-linux-arm64-gnu': 4.3.0 - '@tailwindcss/oxide-linux-arm64-musl': 4.3.0 - '@tailwindcss/oxide-linux-x64-gnu': 4.3.0 - '@tailwindcss/oxide-linux-x64-musl': 4.3.0 - '@tailwindcss/oxide-wasm32-wasi': 4.3.0 - '@tailwindcss/oxide-win32-arm64-msvc': 4.3.0 - '@tailwindcss/oxide-win32-x64-msvc': 4.3.0 - - '@tailwindcss/postcss@4.3.0': - dependencies: - '@alloc/quick-lru': 5.2.0 - '@tailwindcss/node': 4.3.0 - '@tailwindcss/oxide': 4.3.0 - postcss: 8.5.26 - tailwindcss: 4.3.0 - - '@tailwindcss/typography@0.5.19(tailwindcss@4.3.0)': - dependencies: - postcss-selector-parser: 6.0.10 - tailwindcss: 4.3.0 - - '@tanstack/history@1.162.0': {} - - '@tanstack/query-core@5.101.4': {} - - '@tanstack/react-query@5.101.4(react@19.2.7)': - dependencies: - '@tanstack/query-core': 5.101.4 - react: 19.2.7 - - '@tanstack/react-router@1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@tanstack/history': 1.162.0 - '@tanstack/react-store': 0.9.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/router-core': 1.171.15 - isbot: 5.2.1 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - - '@tanstack/react-store@0.9.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@tanstack/store': 0.9.3 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - use-sync-external-store: 1.6.0(react@19.2.7) - - '@tanstack/react-virtual@3.14.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@tanstack/virtual-core': 3.17.1 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - - '@tanstack/router-core@1.171.15': - dependencies: - '@tanstack/history': 1.162.0 - cookie-es: 3.1.1 - seroval: 1.6.8 - seroval-plugins: 1.6.8(seroval@1.6.8) - - '@tanstack/store@0.9.3': {} - - '@tanstack/virtual-core@3.17.1': {} - - '@theguild/remark-mermaid@0.3.0(react@19.2.7)': - dependencies: - mermaid: 11.17.2 - react: 19.2.7 - unist-util-visit: 5.1.0 - - '@theguild/remark-npm2yarn@0.3.3': - dependencies: - npm-to-yarn: 3.0.1 - unist-util-visit: 5.1.0 - - '@tree-sitter-grammars/tree-sitter-yaml@0.7.1(tree-sitter@0.22.4)': - dependencies: - node-addon-api: 8.5.0 - node-gyp-build: 4.8.4 - optionalDependencies: - tree-sitter: 0.22.4 - optional: true - - '@trpc/server@11.16.0(typescript@5.9.3)': - dependencies: - typescript: 5.9.3 - - '@ts-morph/common@0.28.1': - dependencies: - minimatch: 10.2.6 - path-browserify: 1.0.1 - tinyglobby: 0.2.17 - - '@types/chai@5.2.3': - dependencies: - '@types/deep-eql': 4.0.2 - assertion-error: 2.0.1 - - '@types/d3-array@3.2.2': {} - - '@types/d3-axis@3.0.6': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-brush@3.0.6': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-chord@3.0.6': {} - - '@types/d3-color@3.1.3': {} - - '@types/d3-contour@3.0.6': - dependencies: - '@types/d3-array': 3.2.2 - '@types/geojson': 7946.0.16 - - '@types/d3-delaunay@6.0.4': {} - - '@types/d3-dispatch@3.0.7': {} - - '@types/d3-drag@3.0.7': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-dsv@3.0.7': {} - - '@types/d3-ease@3.0.2': {} - - '@types/d3-fetch@3.0.7': - dependencies: - '@types/d3-dsv': 3.0.7 - - '@types/d3-force@3.0.10': {} - - '@types/d3-format@3.0.4': {} - - '@types/d3-geo@3.1.0': - dependencies: - '@types/geojson': 7946.0.16 - - '@types/d3-hierarchy@3.1.7': {} - - '@types/d3-interpolate@3.0.4': - dependencies: - '@types/d3-color': 3.1.3 - - '@types/d3-path@3.1.1': {} - - '@types/d3-polygon@3.0.2': {} - - '@types/d3-quadtree@3.0.6': {} - - '@types/d3-random@3.0.3': {} - - '@types/d3-scale-chromatic@3.1.0': {} - - '@types/d3-scale@4.0.9': - dependencies: - '@types/d3-time': 3.0.4 - - '@types/d3-selection@3.0.11': {} - - '@types/d3-shape@3.1.8': - dependencies: - '@types/d3-path': 3.1.1 - - '@types/d3-time-format@4.0.3': {} - - '@types/d3-time@3.0.4': {} - - '@types/d3-timer@3.0.2': {} - - '@types/d3-transition@3.0.9': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-zoom@3.0.8': - dependencies: - '@types/d3-interpolate': 3.0.4 - '@types/d3-selection': 3.0.11 - - '@types/d3@7.4.3': - dependencies: - '@types/d3-array': 3.2.2 - '@types/d3-axis': 3.0.6 - '@types/d3-brush': 3.0.6 - '@types/d3-chord': 3.0.6 - '@types/d3-color': 3.1.3 - '@types/d3-contour': 3.0.6 - '@types/d3-delaunay': 6.0.4 - '@types/d3-dispatch': 3.0.7 - '@types/d3-drag': 3.0.7 - '@types/d3-dsv': 3.0.7 - '@types/d3-ease': 3.0.2 - '@types/d3-fetch': 3.0.7 - '@types/d3-force': 3.0.10 - '@types/d3-format': 3.0.4 - '@types/d3-geo': 3.1.0 - '@types/d3-hierarchy': 3.1.7 - '@types/d3-interpolate': 3.0.4 - '@types/d3-path': 3.1.1 - '@types/d3-polygon': 3.0.2 - '@types/d3-quadtree': 3.0.6 - '@types/d3-random': 3.0.3 - '@types/d3-scale': 4.0.9 - '@types/d3-scale-chromatic': 3.1.0 - '@types/d3-selection': 3.0.11 - '@types/d3-shape': 3.1.8 - '@types/d3-time': 3.0.4 - '@types/d3-time-format': 4.0.3 - '@types/d3-timer': 3.0.2 - '@types/d3-transition': 3.0.9 - '@types/d3-zoom': 3.0.8 - - '@types/debug@4.1.12': - dependencies: - '@types/ms': 2.1.0 - - '@types/deep-eql@4.0.2': {} - - '@types/dom-speech-recognition@0.0.1': {} - - '@types/estree-jsx@1.0.5': - dependencies: - '@types/estree': 1.0.8 - - '@types/estree@1.0.8': {} - - '@types/estree@1.0.9': {} - - '@types/geojson@7946.0.16': {} - - '@types/google.maps@3.58.1': {} - - '@types/hast@3.0.5': - dependencies: - '@types/unist': 3.0.3 - - '@types/hogan.js@3.0.5': {} - - '@types/katex@0.16.7': {} - - '@types/mdast@4.0.4': - dependencies: - '@types/unist': 3.0.3 - - '@types/mdx@2.0.13': {} - - '@types/ms@2.1.0': {} - - '@types/nlcst@2.0.3': - dependencies: - '@types/unist': 3.0.3 - - '@types/node@22.19.17': - dependencies: - undici-types: 6.21.0 - - '@types/prismjs@1.26.5': {} - - '@types/qs@6.14.0': {} - - '@types/ramda@0.30.2': - dependencies: - types-ramda: 0.30.1 - - '@types/react-dom@19.2.3(@types/react@19.2.16)': - dependencies: - '@types/react': 19.2.16 - - '@types/react-syntax-highlighter@15.5.13': - dependencies: - '@types/react': 19.2.16 - - '@types/react@19.2.16': - dependencies: - csstype: 3.2.3 - - '@types/trusted-types@2.0.7': - optional: true - - '@types/unist@2.0.11': {} - - '@types/unist@3.0.3': {} - - '@types/use-sync-external-store@0.0.6': {} - - '@typescript/vfs@1.6.2(typescript@5.9.3)': - dependencies: - debug: 4.4.3 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@uidotdev/usehooks@2.4.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - - '@ungap/structured-clone@1.3.0': {} - - '@upsetjs/venn.js@2.0.0': - optionalDependencies: - d3-selection: 3.0.0 - d3-transition: 3.0.1(d3-selection@3.0.0) - - '@vercel/oidc@3.2.0': {} - - '@vitest/expect@4.1.8': - dependencies: - '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.1.8 - '@vitest/utils': 4.1.8 - chai: 6.2.2 - tinyrainbow: 3.1.0 - - '@vitest/mocker@4.1.8(vite@7.3.5(@types/node@22.19.17)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.23.8)(yaml@2.8.3))': - dependencies: - '@vitest/spy': 4.1.8 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 7.3.5(@types/node@22.19.17)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.23.8)(yaml@2.8.3) - - '@vitest/pretty-format@4.1.8': - dependencies: - tinyrainbow: 3.1.0 - - '@vitest/runner@4.1.8': - dependencies: - '@vitest/utils': 4.1.8 - pathe: 2.0.3 - - '@vitest/snapshot@4.1.8': - dependencies: - '@vitest/pretty-format': 4.1.8 - '@vitest/utils': 4.1.8 - magic-string: 0.30.21 - pathe: 2.0.3 - - '@vitest/spy@4.1.8': {} - - '@vitest/utils@4.1.8': - dependencies: - '@vitest/pretty-format': 4.1.8 - convert-source-map: 2.0.0 - tinyrainbow: 3.1.0 - - '@workflow/serde@4.1.0': {} - - '@xmldom/xmldom@0.9.12': {} - - abbrev@1.1.1: {} - - acorn-jsx@5.3.2(acorn@8.15.0): - dependencies: - acorn: 8.15.0 - - acorn@8.15.0: {} - - agent-base@6.0.2: - dependencies: - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - ai@7.0.37(zod@4.3.6): - dependencies: - '@ai-sdk/gateway': 4.0.28(zod@4.3.6) - '@ai-sdk/provider': 4.0.3 - '@ai-sdk/provider-utils': 5.0.12(zod@4.3.6) - zod: 4.3.6 - - algoliasearch-helper@3.29.1(algoliasearch@5.53.0): - dependencies: - '@algolia/events': 4.0.1 - algoliasearch: 5.53.0 - - algoliasearch@5.53.0: - dependencies: - '@algolia/abtesting': 1.19.0 - '@algolia/client-abtesting': 5.53.0 - '@algolia/client-analytics': 5.53.0 - '@algolia/client-common': 5.53.0 - '@algolia/client-insights': 5.53.0 - '@algolia/client-personalization': 5.53.0 - '@algolia/client-query-suggestions': 5.53.0 - '@algolia/client-search': 5.53.0 - '@algolia/ingestion': 1.53.0 - '@algolia/monitoring': 1.53.0 - '@algolia/recommend': 5.53.0 - '@algolia/requester-browser-xhr': 5.53.0 - '@algolia/requester-fetch': 5.53.0 - '@algolia/requester-node-http': 5.53.0 - - ansi-escapes@7.2.0: - dependencies: - environment: 1.1.0 - - ansi-regex@6.2.2: {} - - ansi-styles@6.2.3: {} - - apg-lite@1.0.5: {} - - arg@5.0.2: {} - - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - - argparse@2.0.1: {} - - aria-hidden@1.2.6: - dependencies: - tslib: 2.8.1 - - array-iterate@2.0.1: {} - - assertion-error@2.0.1: {} - - astring@1.9.0: {} - - asynckit@0.4.0: {} - - autolinker@3.16.2: - dependencies: - tslib: 2.8.1 - - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.1.0 - - axios@1.20.0: - dependencies: - follow-redirects: 1.16.0 - form-data: 4.0.6 - https-proxy-agent: 5.0.1 - proxy-from-env: 2.1.0 - transitivePeerDependencies: - - debug - - supports-color - - bail@2.0.2: {} - - balanced-match@4.0.4: {} - - base64-js@1.5.1: {} - - baseline-browser-mapping@2.10.33: {} - - before-after-hook@4.0.0: {} - - better-react-mathjax@2.3.0(react@19.2.7): - dependencies: - mathjax-full: 3.2.2 - react: 19.2.7 - - brace-expansion@5.0.12: - dependencies: - balanced-match: 4.0.4 - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bind@1.0.8: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - get-intrinsic: 1.3.0 - set-function-length: 1.2.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - - caniuse-lite@1.0.30001760: {} - - ccount@2.0.1: {} - - chai@6.2.2: {} - - chalk@5.6.2: {} - - character-entities-html4@2.1.0: {} - - character-entities-legacy@3.0.0: {} - - character-entities@2.0.2: {} - - character-reference-invalid@2.0.1: {} - - citty@0.2.2: {} - - class-variance-authority@0.7.1: - dependencies: - clsx: 2.1.1 - - classnames@2.5.1: {} - - cli-cursor@5.0.0: - dependencies: - restore-cursor: 5.1.0 - - cli-spinners@3.3.0: {} - - cli-truncate@5.1.1: - dependencies: - slice-ansi: 7.1.2 - string-width: 8.1.0 - - client-only@0.0.1: {} - - clipboardy@4.0.0: - dependencies: - execa: 8.0.1 - is-wsl: 3.1.0 - is64bit: 2.0.0 - - clsx@2.1.1: {} - - cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.16)(react@19.2.7) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.16)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - transitivePeerDependencies: - - '@types/react' - - '@types/react-dom' - - code-block-writer@13.0.3: {} - - collapse-white-space@2.1.0: {} - - colorette@2.0.20: {} - - combined-stream@1.0.8: - dependencies: - delayed-stream: 1.0.0 - - comma-separated-tokens@2.0.3: {} - - commander@13.1.0: {} - - commander@14.0.3: {} - - commander@7.2.0: {} - - commander@8.3.0: {} - - compute-scroll-into-view@3.1.1: {} - - confbox@0.1.8: {} - - convert-source-map@2.0.0: {} - - cookie-es@3.1.1: {} - - copy-to-clipboard@3.3.3: - dependencies: - toggle-selection: 1.0.6 - - core-js-pure@3.47.0: {} - - core-js@3.47.0: {} - - cose-base@1.0.3: - dependencies: - layout-base: 1.0.2 - - cose-base@2.2.0: - dependencies: - layout-base: 2.0.1 - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - css.escape@1.5.1: {} - - cssesc@3.0.0: {} - - csstype@3.2.3: {} - - cytoscape-cose-bilkent@4.1.0(cytoscape@3.34.2): - dependencies: - cose-base: 1.0.3 - cytoscape: 3.34.2 - - cytoscape-fcose@2.2.0(cytoscape@3.34.2): - dependencies: - cose-base: 2.2.0 - cytoscape: 3.34.2 - - cytoscape@3.34.2: {} - - d3-array@2.12.1: - dependencies: - internmap: 1.0.1 - - d3-array@3.2.4: - dependencies: - internmap: 2.0.3 - - d3-axis@3.0.0: {} - - d3-brush@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-drag: 3.0.0 - d3-interpolate: 3.0.1 - d3-selection: 3.0.0 - d3-transition: 3.0.1(d3-selection@3.0.0) - - d3-chord@3.0.1: - dependencies: - d3-path: 3.1.0 - - d3-color@3.1.0: {} - - d3-contour@4.0.2: - dependencies: - d3-array: 3.2.4 - - d3-delaunay@6.0.4: - dependencies: - delaunator: 5.0.1 - - d3-dispatch@3.0.1: {} - - d3-drag@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-selection: 3.0.0 - - d3-dsv@3.0.1: - dependencies: - commander: 7.2.0 - iconv-lite: 0.6.3 - rw: 1.3.3 - - d3-ease@3.0.1: {} - - d3-fetch@3.0.1: - dependencies: - d3-dsv: 3.0.1 - - d3-force@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-quadtree: 3.0.1 - d3-timer: 3.0.1 - - d3-format@3.1.0: {} - - d3-geo@3.1.1: - dependencies: - d3-array: 3.2.4 - - d3-hierarchy@3.1.2: {} - - d3-interpolate@3.0.1: - dependencies: - d3-color: 3.1.0 - - d3-path@1.0.9: {} - - d3-path@3.1.0: {} - - d3-polygon@3.0.1: {} - - d3-quadtree@3.0.1: {} - - d3-random@3.0.1: {} - - d3-sankey@0.12.3: - dependencies: - d3-array: 2.12.1 - d3-shape: 1.3.7 - - d3-scale-chromatic@3.1.0: - dependencies: - d3-color: 3.1.0 - d3-interpolate: 3.0.1 - - d3-scale@4.0.2: - dependencies: - d3-array: 3.2.4 - d3-format: 3.1.0 - d3-interpolate: 3.0.1 - d3-time: 3.1.0 - d3-time-format: 4.1.0 - - d3-selection@3.0.0: {} - - d3-shape@1.3.7: - dependencies: - d3-path: 1.0.9 - - d3-shape@3.2.0: - dependencies: - d3-path: 3.1.0 - - d3-time-format@4.1.0: - dependencies: - d3-time: 3.1.0 - - d3-time@3.1.0: - dependencies: - d3-array: 3.2.4 - - d3-timer@3.0.1: {} - - d3-transition@3.0.1(d3-selection@3.0.0): - dependencies: - d3-color: 3.1.0 - d3-dispatch: 3.0.1 - d3-ease: 3.0.1 - d3-interpolate: 3.0.1 - d3-selection: 3.0.0 - d3-timer: 3.0.1 - - d3-zoom@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-drag: 3.0.0 - d3-interpolate: 3.0.1 - d3-selection: 3.0.0 - d3-transition: 3.0.1(d3-selection@3.0.0) - - d3@7.9.0: - dependencies: - d3-array: 3.2.4 - d3-axis: 3.0.0 - d3-brush: 3.0.0 - d3-chord: 3.0.1 - d3-color: 3.1.0 - d3-contour: 4.0.2 - d3-delaunay: 6.0.4 - d3-dispatch: 3.0.1 - d3-drag: 3.0.0 - d3-dsv: 3.0.1 - d3-ease: 3.0.1 - d3-fetch: 3.0.1 - d3-force: 3.0.0 - d3-format: 3.1.0 - d3-geo: 3.1.1 - d3-hierarchy: 3.1.2 - d3-interpolate: 3.0.1 - d3-path: 3.1.0 - d3-polygon: 3.0.1 - d3-quadtree: 3.0.1 - d3-random: 3.0.1 - d3-scale: 4.0.2 - d3-scale-chromatic: 3.1.0 - d3-selection: 3.0.0 - d3-shape: 3.2.0 - d3-time: 3.1.0 - d3-time-format: 4.1.0 - d3-timer: 3.0.1 - d3-transition: 3.0.1(d3-selection@3.0.0) - d3-zoom: 3.0.0 - - dagre-d3-es@7.0.14: - dependencies: - d3: 7.9.0 - lodash-es: 4.18.1 - - date-fns@4.4.0: {} - - dayjs@1.11.23: {} - - debug@4.4.3: - dependencies: - ms: 2.1.3 - - decimal.js-light@2.5.1: {} - - decode-named-character-reference@1.2.0: - dependencies: - character-entities: 2.0.2 - - deep-extend@0.6.0: {} - - deepmerge@4.3.1: {} - - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - - delaunator@5.0.1: - dependencies: - robust-predicates: 3.0.2 - - delayed-stream@1.0.0: {} - - dequal@2.0.3: {} - - detect-libc@2.1.2: {} - - detect-node-es@1.1.0: {} - - devlop@1.1.0: - dependencies: - dequal: 2.0.3 - - dompurify@3.4.14: - optionalDependencies: - '@types/trusted-types': 2.0.7 - - dotenv@17.4.2: {} - - drange@1.1.1: {} - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - - emoji-regex@10.6.0: {} - - enhanced-resolve@5.21.0: - dependencies: - graceful-fs: 4.2.11 - tapable: 2.3.3 - - entities@6.0.1: {} - - environment@1.1.0: {} - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-module-lexer@2.0.0: {} - - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.4 - - es-toolkit@1.49.0: {} - - 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.15.0 - esast-util-from-estree: 2.0.0 - vfile-message: 4.0.3 - - esbuild@0.28.1: - optionalDependencies: - '@esbuild/aix-ppc64': 0.28.1 - '@esbuild/android-arm': 0.28.1 - '@esbuild/android-arm64': 0.28.1 - '@esbuild/android-x64': 0.28.1 - '@esbuild/darwin-arm64': 0.28.1 - '@esbuild/darwin-x64': 0.28.1 - '@esbuild/freebsd-arm64': 0.28.1 - '@esbuild/freebsd-x64': 0.28.1 - '@esbuild/linux-arm': 0.28.1 - '@esbuild/linux-arm64': 0.28.1 - '@esbuild/linux-ia32': 0.28.1 - '@esbuild/linux-loong64': 0.28.1 - '@esbuild/linux-mips64el': 0.28.1 - '@esbuild/linux-ppc64': 0.28.1 - '@esbuild/linux-riscv64': 0.28.1 - '@esbuild/linux-s390x': 0.28.1 - '@esbuild/linux-x64': 0.28.1 - '@esbuild/netbsd-arm64': 0.28.1 - '@esbuild/netbsd-x64': 0.28.1 - '@esbuild/openbsd-arm64': 0.28.1 - '@esbuild/openbsd-x64': 0.28.1 - '@esbuild/openharmony-arm64': 0.28.1 - '@esbuild/sunos-x64': 0.28.1 - '@esbuild/win32-arm64': 0.28.1 - '@esbuild/win32-ia32': 0.28.1 - '@esbuild/win32-x64': 0.28.1 - - escape-string-regexp@5.0.0: {} - - esm@3.2.25: {} - - 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@2.1.0: {} - - estree-util-is-identifier-name@3.0.0: {} - - estree-util-scope@1.0.0: - dependencies: - '@types/estree': 1.0.8 - 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.8 - - eventemitter3@5.0.4: {} - - eventsource-parser@3.1.0: {} - - execa@8.0.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 - - expect-type@1.3.0: {} - - extend@3.0.2: {} - - fast-content-type-parse@3.0.0: {} - - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fast-json-patch@3.1.1: {} - - fastdom@1.0.12: - dependencies: - strictdom: 1.0.1 - - fastq@1.19.1: - dependencies: - reusify: 1.1.0 - - fault@1.0.4: - dependencies: - format: 0.2.2 - - fault@2.0.1: - dependencies: - format: 0.2.2 - - fdir@6.5.0(picomatch@4.0.5): - optionalDependencies: - picomatch: 4.0.5 - - fflate@0.4.9: {} - - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - follow-redirects@1.16.0: {} - - for-each@0.3.5: - dependencies: - is-callable: 1.2.7 - - form-data@4.0.6: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - hasown: 2.0.4 - mime-types: 2.1.35 - - format@0.2.2: {} - - framer-motion@12.40.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): - dependencies: - motion-dom: 12.40.0 - motion-utils: 12.39.0 - tslib: 2.8.1 - optionalDependencies: - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - - fsevents@2.3.3: - optional: true - - function-bind@1.1.2: {} - - get-east-asian-width@1.4.0: {} - - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.4 - math-intrinsics: 1.1.0 - - get-nonce@1.0.1: {} - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - - get-stream@8.0.1: {} - - github-slugger@2.0.0: {} - - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - gopd@1.2.0: {} - - graceful-fs@4.2.11: {} - - hachure-fill@0.5.2: {} - - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.1 - - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - - hasown@2.0.4: - dependencies: - function-bind: 1.1.2 - - hast-util-from-dom@5.0.1: - dependencies: - '@types/hast': 3.0.5 - hastscript: 9.0.1 - web-namespaces: 2.0.1 - - hast-util-from-html-isomorphic@2.0.0: - dependencies: - '@types/hast': 3.0.5 - hast-util-from-dom: 5.0.1 - hast-util-from-html: 2.0.3 - unist-util-remove-position: 5.0.0 - - hast-util-from-html@2.0.3: - dependencies: - '@types/hast': 3.0.5 - devlop: 1.1.0 - hast-util-from-parse5: 8.0.3 - parse5: 7.3.0 - vfile: 6.0.3 - vfile-message: 4.0.3 - - hast-util-from-parse5@8.0.3: - dependencies: - '@types/hast': 3.0.5 - '@types/unist': 3.0.3 - devlop: 1.1.0 - hastscript: 9.0.1 - property-information: 7.1.0 - vfile: 6.0.3 - vfile-location: 5.0.3 - web-namespaces: 2.0.1 - - hast-util-is-element@3.0.0: - dependencies: - '@types/hast': 3.0.5 - - hast-util-parse-selector@4.0.0: - dependencies: - '@types/hast': 3.0.5 - - hast-util-raw@9.1.0: - dependencies: - '@types/hast': 3.0.5 - '@types/unist': 3.0.3 - '@ungap/structured-clone': 1.3.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-sanitize@5.0.2: - dependencies: - '@types/hast': 3.0.5 - '@ungap/structured-clone': 1.3.0 - unist-util-position: 5.0.0 - - hast-util-to-estree@3.1.3: - dependencies: - '@types/estree': 1.0.8 - '@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.1.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.1.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.8 - '@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.1.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.1.0 - space-separated-tokens: 2.0.2 - web-namespaces: 2.0.1 - zwitch: 2.0.4 - - hast-util-to-string@3.0.1: - dependencies: - '@types/hast': 3.0.5 - - hast-util-to-text@4.0.2: - dependencies: - '@types/hast': 3.0.5 - '@types/unist': 3.0.3 - hast-util-is-element: 3.0.0 - unist-util-find-after: 5.0.0 - - 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.1.0 - space-separated-tokens: 2.0.2 - - highlight.js@10.7.3: {} - - highlightjs-vue@1.0.0: {} - - hogan.js@3.0.2: - dependencies: - mkdirp: 0.3.0 - nopt: 1.0.10 - - htm@3.1.1: {} - - html-url-attributes@3.0.1: {} - - html-void-elements@3.0.0: {} - - https-proxy-agent@5.0.1: - dependencies: - agent-base: 6.0.2 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - human-signals@5.0.0: {} - - husky@9.1.7: {} - - iconv-lite@0.6.3: - dependencies: - safer-buffer: 2.1.2 - - ieee754@1.2.1: {} - - immer@10.2.0: {} - - immer@11.1.15: {} - - immutable@4.3.9: {} - - inherits@2.0.4: {} - - inline-style-parser@0.2.7: {} - - instantsearch-ui-components@0.29.0(react@19.2.7): - dependencies: - '@swc/helpers': 0.5.18 - markdown-to-jsx: 7.7.17(react@19.2.7) - transitivePeerDependencies: - - react - - instantsearch.js@4.100.0(algoliasearch@5.53.0): - dependencies: - '@algolia/events': 4.0.1 - '@swc/helpers': 0.5.18 - '@types/dom-speech-recognition': 0.0.1 - '@types/google.maps': 3.58.1 - '@types/hogan.js': 3.0.5 - '@types/qs': 6.14.0 - algoliasearch: 5.53.0 - algoliasearch-helper: 3.29.1(algoliasearch@5.53.0) - hogan.js: 3.0.2 - htm: 3.1.1 - instantsearch-ui-components: 0.29.0(react@19.2.7) - preact: 10.29.1 - qs: 6.16.0 - react: 19.2.7 - search-insights: 2.17.3 - - internmap@1.0.1: {} - - internmap@2.0.3: {} - - invariant@2.2.4: - dependencies: - loose-envify: 1.4.0 - - is-alphabetical@2.0.1: {} - - is-alphanumerical@2.0.1: - dependencies: - is-alphabetical: 2.0.1 - is-decimal: 2.0.1 - - is-callable@1.2.7: {} - - is-decimal@2.0.1: {} - - is-docker@3.0.0: {} - - is-extglob@2.1.1: {} - - is-fullwidth-code-point@5.1.0: - dependencies: - get-east-asian-width: 1.4.0 - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - - is-hexadecimal@2.0.1: {} - - is-inside-container@1.0.0: - dependencies: - is-docker: 3.0.0 - - is-interactive@2.0.0: {} - - is-number@7.0.0: {} - - is-plain-obj@4.1.0: {} - - is-stream@3.0.0: {} - - is-typed-array@1.1.15: - dependencies: - which-typed-array: 1.1.19 - - is-unicode-supported@2.1.0: {} - - is-wsl@3.1.0: - dependencies: - is-inside-container: 1.0.0 - - is64bit@2.0.0: - dependencies: - system-architecture: 0.1.0 - - isarray@2.0.5: {} - - isbot@5.2.1: {} - - isexe@2.0.0: {} - - jiti@2.6.1: {} - - js-file-download@0.4.12: {} - - js-tokens@4.0.0: {} - - js-yaml@4.3.2: - dependencies: - argparse: 2.0.1 - - json-schema-to-ts@3.1.1: - dependencies: - '@babel/runtime': 7.28.4 - ts-algebra: 2.0.0 - - json-schema@0.4.0: {} - - jsonc-parser@3.3.1: {} - - katex@0.16.27: - dependencies: - commander: 8.3.0 - - katex@0.16.47: - dependencies: - commander: 8.3.0 - - khroma@2.1.0: {} - - layout-base@1.0.2: {} - - layout-base@2.0.1: {} - - 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 - - lint-staged@16.4.0: - dependencies: - commander: 14.0.3 - listr2: 9.0.5 - picomatch: 4.0.4 - string-argv: 0.3.2 - tinyexec: 1.1.1 - yaml: 2.8.3 - - listr2@9.0.5: - dependencies: - cli-truncate: 5.1.1 - colorette: 2.0.20 - eventemitter3: 5.0.4 - log-update: 6.1.0 - rfdc: 1.4.1 - wrap-ansi: 9.0.2 - - lodash-es@4.18.1: {} - - lodash.debounce@4.0.8: {} - - lodash@4.18.1: {} - - log-symbols@7.0.1: - dependencies: - is-unicode-supported: 2.1.0 - yoctocolors: 2.1.2 - - log-update@6.1.0: - dependencies: - ansi-escapes: 7.2.0 - cli-cursor: 5.0.0 - slice-ansi: 7.1.2 - strip-ansi: 7.1.2 - wrap-ansi: 9.0.2 - - long@5.3.2: {} - - longest-streak@3.1.0: {} - - loose-envify@1.4.0: - dependencies: - js-tokens: 4.0.0 - - lowlight@1.20.0: - dependencies: - fault: 1.0.4 - highlight.js: 10.7.3 - - lucide-react@0.577.0(react@19.2.7): - dependencies: - react: 19.2.7 - - magic-string@0.30.21: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - - markdown-extensions@2.0.0: {} - - markdown-table@3.0.4: {} - - markdown-to-jsx@7.7.17(react@19.2.7): - optionalDependencies: - react: 19.2.7 - - marked@16.4.2: {} - - marked@17.0.3: {} - - math-intrinsics@1.1.0: {} - - mathjax-full@3.2.2: - dependencies: - esm: 3.2.25 - mhchemparser: 4.2.1 - mj-context-menu: 0.6.1 - speech-rule-engine: 4.1.2 - - 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.2: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - decode-named-character-reference: 1.2.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-frontmatter@2.0.1: - dependencies: - '@types/mdast': 4.0.4 - devlop: 1.1.0 - escape-string-regexp: 5.0.0 - mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.2 - micromark-extension-frontmatter: 2.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.2 - 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.2 - 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.2 - 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.2 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm@3.1.0: - dependencies: - mdast-util-from-markdown: 2.0.2 - 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-math@3.0.0: - dependencies: - '@types/hast': 3.0.5 - '@types/mdast': 4.0.4 - devlop: 1.1.0 - longest-streak: 3.1.0 - mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.2 - unist-util-remove-position: 5.0.0 - 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.2 - 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.2 - 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.2 - 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.2 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-newline-to-break@2.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-find-and-replace: 3.0.2 - - 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.3.0 - devlop: 1.1.0 - micromark-util-sanitize-uri: 2.0.1 - trim-lines: 3.0.1 - unist-util-position: 5.0.0 - unist-util-visit: 5.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 - - merge-stream@2.0.0: {} - - merge2@1.4.1: {} - - mermaid@11.17.2: - dependencies: - '@braintree/sanitize-url': 7.1.2 - '@iconify/utils': 3.1.0 - '@mermaid-js/parser': 1.2.1 - '@types/d3': 7.4.3 - '@upsetjs/venn.js': 2.0.0 - cytoscape: 3.34.2 - cytoscape-cose-bilkent: 4.1.0(cytoscape@3.34.2) - cytoscape-fcose: 2.2.0(cytoscape@3.34.2) - d3: 7.9.0 - d3-sankey: 0.12.3 - dagre-d3-es: 7.0.14 - dayjs: 1.11.23 - dompurify: 3.4.14 - es-toolkit: 1.49.0 - fastdom: 1.0.12 - katex: 0.16.47 - khroma: 2.1.0 - marked: 16.4.2 - roughjs: 4.6.6 - stylis: 4.3.6 - ts-dedent: 2.2.0 - uuid: 11.1.1 - - mhchemparser@4.2.1: {} - - micromark-core-commonmark@2.0.3: - dependencies: - decode-named-character-reference: 1.2.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-frontmatter@2.0.0: - dependencies: - fault: 2.0.1 - micromark-util-character: 2.1.1 - 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-math@3.1.0: - dependencies: - '@types/katex': 0.16.7 - devlop: 1.1.0 - katex: 0.16.27 - 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-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.15.0 - acorn-jsx: 5.3.2(acorn@8.15.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.2.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.12 - debug: 4.4.3 - decode-named-character-reference: 1.2.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 - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.2 - - mime-db@1.52.0: {} - - mime-types@2.1.35: - dependencies: - mime-db: 1.52.0 - - mimic-fn@4.0.0: {} - - mimic-function@5.0.1: {} - - minim@0.23.8: - dependencies: - lodash: 4.18.1 - - minimatch@10.2.6: - dependencies: - brace-expansion: 5.0.12 - - mj-context-menu@0.6.1: {} - - mkdirp@0.3.0: {} - - mlly@1.8.0: - dependencies: - acorn: 8.15.0 - pathe: 2.0.3 - pkg-types: 1.3.1 - ufo: 1.6.1 - - motion-dom@12.40.0: - dependencies: - motion-utils: 12.39.0 - - motion-utils@12.39.0: {} - - motion@12.40.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): - dependencies: - framer-motion: 12.40.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - tslib: 2.8.1 - optionalDependencies: - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - - ms@2.1.3: {} - - nanoid@3.3.18: {} - - negotiator@1.0.0: {} - - neotraverse@1.0.1: {} - - next-themes@0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7): - dependencies: - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - - next-validate-link@1.6.6(@types/mdast@4.0.4)(@types/unist@3.0.3): - dependencies: - js-yaml: 4.3.2 - picocolors: 1.1.1 - remark: 15.0.1 - remark-gfm: 4.0.1 - remark-mdx: 3.1.1 - tinyglobby: 0.2.17 - unified: 11.0.5 - unist-util-visit: 5.1.0 - optionalDependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - transitivePeerDependencies: - - supports-color - - next@16.3.8(@opentelemetry/api@1.9.0)(@types/node@22.19.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): - dependencies: - '@next/env': 16.3.8 - '@swc/helpers': 0.5.23 - baseline-browser-mapping: 2.10.33 - caniuse-lite: 1.0.30001760 - postcss: 8.5.26 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - styled-jsx: 5.1.6(react@19.2.7) - optionalDependencies: - '@next/swc-darwin-arm64': 16.3.8 - '@next/swc-darwin-x64': 16.3.8 - '@next/swc-linux-arm64-gnu': 16.3.8 - '@next/swc-linux-arm64-musl': 16.3.8 - '@next/swc-linux-x64-gnu': 16.3.8 - '@next/swc-linux-x64-musl': 16.3.8 - '@next/swc-win32-arm64-msvc': 16.3.8 - '@next/swc-win32-x64-msvc': 16.3.8 - '@opentelemetry/api': 1.9.0 - sharp: 0.35.5(@types/node@22.19.17) - transitivePeerDependencies: - - '@babel/core' - - '@types/node' - - babel-plugin-macros - - nextra-theme-docs@4.6.1(@types/react@19.2.16)(immer@11.1.15)(next@16.3.8(@opentelemetry/api@1.9.0)(@types/node@22.19.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(nextra@4.6.1(next@16.3.8(@opentelemetry/api@1.9.0)(@types/node@22.19.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7)): - dependencies: - '@headlessui/react': 2.2.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - clsx: 2.1.1 - next: 16.3.8(@opentelemetry/api@1.9.0)(@types/node@22.19.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - next-themes: 0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - nextra: 4.6.1(next@16.3.8(@opentelemetry/api@1.9.0)(@types/node@22.19.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3) - react: 19.2.7 - react-compiler-runtime: 19.1.0-rc.3(react@19.2.7) - react-dom: 19.2.7(react@19.2.7) - scroll-into-view-if-needed: 3.1.0 - zod: 4.3.6 - zustand: 5.0.14(@types/react@19.2.16)(immer@11.1.15)(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7)) - transitivePeerDependencies: - - '@types/react' - - immer - - use-sync-external-store - - nextra@4.6.1(next@16.3.8(@opentelemetry/api@1.9.0)(@types/node@22.19.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3): - dependencies: - '@formatjs/intl-localematcher': 0.6.2 - '@headlessui/react': 2.2.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@mdx-js/mdx': 3.1.1 - '@napi-rs/simple-git': 0.1.22 - '@shikijs/twoslash': 3.20.0(typescript@5.9.3) - '@theguild/remark-mermaid': 0.3.0(react@19.2.7) - '@theguild/remark-npm2yarn': 0.3.3 - better-react-mathjax: 2.3.0(react@19.2.7) - clsx: 2.1.1 - estree-util-to-js: 2.0.0 - estree-util-value-to-estree: 3.5.0 - fast-glob: 3.3.3 - github-slugger: 2.0.0 - hast-util-to-estree: 3.1.3 - katex: 0.16.27 - mdast-util-from-markdown: 2.0.2 - mdast-util-gfm: 3.1.0 - mdast-util-to-hast: 13.2.1 - negotiator: 1.0.0 - next: 16.3.8(@opentelemetry/api@1.9.0)(@types/node@22.19.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: 19.2.7 - react-compiler-runtime: 19.1.0-rc.3(react@19.2.7) - react-dom: 19.2.7(react@19.2.7) - react-medium-image-zoom: 5.4.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - rehype-katex: 7.0.1 - rehype-pretty-code: 0.14.1(shiki@3.20.0) - rehype-raw: 7.0.0 - remark-frontmatter: 5.0.0 - remark-gfm: 4.0.1 - remark-math: 6.0.0 - remark-reading-time: 2.0.2 - remark-smartypants: 3.0.2 - server-only: 0.0.1 - shiki: 3.20.0 - slash: 5.1.0 - title: 4.0.1 - ts-morph: 27.0.2 - unist-util-remove: 4.0.0 - unist-util-visit: 5.1.0 - unist-util-visit-children: 3.0.0 - yaml: 2.8.3 - zod: 4.3.6 - transitivePeerDependencies: - - supports-color - - typescript - - nlcst-to-string@4.0.0: - dependencies: - '@types/nlcst': 2.0.3 - - node-abort-controller@3.1.1: {} - - node-addon-api@8.5.0: - optional: true - - node-gyp-build@4.8.4: - optional: true - - nopt@1.0.10: - dependencies: - abbrev: 1.1.1 - - npm-run-path@5.3.0: - dependencies: - path-key: 4.0.0 - - npm-to-yarn@3.0.1: {} - - nypm@0.6.5: - dependencies: - citty: 0.2.2 - pathe: 2.0.3 - tinyexec: 1.1.1 - - object-assign@4.1.1: {} - - object-inspect@1.13.4: {} - - obug@2.1.1: {} - - onetime@6.0.0: - dependencies: - mimic-fn: 4.0.0 - - onetime@7.0.0: - dependencies: - mimic-function: 5.0.1 - - 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 - - openai@6.41.0(zod@4.3.6): - optionalDependencies: - zod: 4.3.6 - - openapi-path-templating@2.2.1: - dependencies: - apg-lite: 1.0.5 - - openapi-server-url-templating@1.3.0: - dependencies: - apg-lite: 1.0.5 - - ora@9.4.0: - dependencies: - chalk: 5.6.2 - cli-cursor: 5.0.0 - cli-spinners: 3.3.0 - is-interactive: 2.0.0 - is-unicode-supported: 2.1.0 - log-symbols: 7.0.1 - stdin-discarder: 0.3.2 - string-width: 8.1.0 - - package-manager-detector@1.6.0: {} - - parse-entities@4.0.2: - dependencies: - '@types/unist': 2.0.11 - character-entities-legacy: 3.0.0 - character-reference-invalid: 2.0.1 - decode-named-character-reference: 1.2.0 - is-alphanumerical: 2.0.1 - is-decimal: 2.0.1 - is-hexadecimal: 2.0.1 - - parse-latin@7.0.0: - dependencies: - '@types/nlcst': 2.0.3 - '@types/unist': 3.0.3 - nlcst-to-string: 4.0.0 - unist-util-modify-children: 4.0.0 - unist-util-visit-children: 3.0.0 - vfile: 6.0.3 - - parse-numeric-range@1.3.0: {} - - parse5@7.3.0: - dependencies: - entities: 6.0.1 - - path-browserify@1.0.1: {} - - path-data-parser@0.1.0: {} - - path-key@3.1.1: {} - - path-key@4.0.0: {} - - pathe@2.0.3: {} - - picocolors@1.1.1: {} - - picomatch@2.3.2: {} - - picomatch@4.0.4: {} - - picomatch@4.0.5: {} - - pkce-challenge@5.0.1: {} - - pkg-types@1.3.1: - dependencies: - confbox: 0.1.8 - mlly: 1.8.0 - pathe: 2.0.3 - - points-on-curve@0.2.0: {} - - points-on-path@0.2.1: - dependencies: - path-data-parser: 0.1.0 - points-on-curve: 0.2.0 - - possible-typed-array-names@1.1.0: {} - - postcss-selector-parser@6.0.10: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - - postcss@8.5.26: - dependencies: - nanoid: 3.3.18 - picocolors: 1.1.1 - source-map-js: 1.2.2 - - posthog-js@1.379.0: - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.208.0 - '@opentelemetry/exporter-logs-otlp-http': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) - '@posthog/core': 1.30.3 - '@posthog/types': 1.379.0 - core-js: 3.47.0 - dompurify: 3.4.14 - fflate: 0.4.9 - preact: 10.29.1 - query-selector-shadow-dom: 1.0.1 - web-vitals: 5.2.0 - - posthog-node@5.35.12: - dependencies: - '@posthog/core': 1.30.3 - - preact@10.29.1: {} - - prismjs@1.30.0: {} - - prop-types@15.8.1: - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 - - property-information@7.1.0: {} - - protobufjs@7.6.5: - dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/base64': 1.1.2 - '@protobufjs/codegen': 2.0.5 - '@protobufjs/eventemitter': 1.1.1 - '@protobufjs/fetch': 1.1.1 - '@protobufjs/float': 1.0.2 - '@protobufjs/path': 1.1.2 - '@protobufjs/pool': 1.1.0 - '@protobufjs/utf8': 1.1.2 - '@types/node': 22.19.17 - long: 5.3.2 - - proxy-from-env@2.1.0: {} - - qs@6.16.0: - dependencies: - es-define-property: 1.0.1 - side-channel: 1.1.1 - - query-selector-shadow-dom@1.0.1: {} - - querystringify@2.2.0: {} - - queue-microtask@1.2.3: {} - - ramda-adjunct@5.1.0(ramda@0.30.1): - dependencies: - ramda: 0.30.1 - - ramda@0.30.1: {} - - randexp@0.5.3: - dependencies: - drange: 1.1.1 - ret: 0.2.2 - - randombytes@2.1.0: - dependencies: - safe-buffer: 5.2.1 - - react-compiler-runtime@19.1.0-rc.3(react@19.2.7): - dependencies: - react: 19.2.7 - - react-copy-to-clipboard@5.1.1(react@19.2.7): - dependencies: - copy-to-clipboard: 3.3.3 - prop-types: 15.8.1 - react: 19.2.7 - - react-day-picker@10.0.1(@types/react@19.2.16)(react@19.2.7): - dependencies: - '@date-fns/tz': 1.4.1 - date-fns: 4.4.0 - react: 19.2.7 - optionalDependencies: - '@types/react': 19.2.16 - - react-debounce-input@3.3.0(react@19.2.7): - dependencies: - lodash.debounce: 4.0.8 - prop-types: 15.8.1 - react: 19.2.7 - - react-dom@19.2.7(react@19.2.7): - dependencies: - react: 19.2.7 - scheduler: 0.27.0 - - react-hook-form@7.77.0(react@19.2.7): - dependencies: - react: 19.2.7 - - react-immutable-proptypes@2.2.0(immutable@4.3.9): - dependencies: - immutable: 4.3.9 - invariant: 2.2.4 - - react-immutable-pure-component@2.2.2(immutable@4.3.9)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): - dependencies: - immutable: 4.3.9 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - - react-inspector@6.0.2(react@19.2.7): - dependencies: - react: 19.2.7 - - react-instantsearch-core@7.34.0(algoliasearch@5.53.0)(react@19.2.7): - dependencies: - '@swc/helpers': 0.5.18 - algoliasearch: 5.53.0 - algoliasearch-helper: 3.29.1(algoliasearch@5.53.0) - instantsearch.js: 4.100.0(algoliasearch@5.53.0) - react: 19.2.7 - use-sync-external-store: 1.6.0(react@19.2.7) - - react-instantsearch@7.34.0(algoliasearch@5.53.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): - dependencies: - '@swc/helpers': 0.5.18 - algoliasearch: 5.53.0 - instantsearch-ui-components: 0.29.0(react@19.2.7) - instantsearch.js: 4.100.0(algoliasearch@5.53.0) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - react-instantsearch-core: 7.34.0(algoliasearch@5.53.0)(react@19.2.7) - - react-is@16.13.1: {} - - react-markdown@10.1.0(@types/react@19.2.16)(react@19.2.7): - dependencies: - '@types/hast': 3.0.5 - '@types/mdast': 4.0.4 - '@types/react': 19.2.16 - devlop: 1.1.0 - hast-util-to-jsx-runtime: 2.3.6 - html-url-attributes: 3.0.1 - mdast-util-to-hast: 13.2.1 - react: 19.2.7 - remark-parse: 11.0.0 - remark-rehype: 11.1.2 - unified: 11.0.5 - unist-util-visit: 5.1.0 - vfile: 6.0.3 - transitivePeerDependencies: - - supports-color - - react-medium-image-zoom@5.4.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): - dependencies: - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - - react-redux@9.3.0(@types/react@19.2.16)(react@19.2.7)(redux@5.0.1): - dependencies: - '@types/use-sync-external-store': 0.0.6 - react: 19.2.7 - use-sync-external-store: 1.6.0(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.16 - redux: 5.0.1 - - react-remove-scroll-bar@2.3.8(@types/react@19.2.16)(react@19.2.7): - dependencies: - react: 19.2.7 - react-style-singleton: 2.2.3(@types/react@19.2.16)(react@19.2.7) - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.16 - - react-remove-scroll@2.7.2(@types/react@19.2.16)(react@19.2.7): - dependencies: - react: 19.2.7 - react-remove-scroll-bar: 2.3.8(@types/react@19.2.16)(react@19.2.7) - react-style-singleton: 2.2.3(@types/react@19.2.16)(react@19.2.7) - tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.2.16)(react@19.2.7) - use-sidecar: 1.1.3(@types/react@19.2.16)(react@19.2.7) - optionalDependencies: - '@types/react': 19.2.16 - - react-resizable-panels@4.11.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7): - dependencies: - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - - react-shiki@0.10.1(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): - dependencies: - clsx: 2.1.1 - dequal: 2.0.3 - hast-util-to-jsx-runtime: 2.3.6 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - shiki: 4.1.0 - unist-util-visit: 5.1.0 - optionalDependencies: - '@types/react': 19.2.16 - '@types/react-dom': 19.2.3(@types/react@19.2.16) - transitivePeerDependencies: - - supports-color - - react-style-singleton@2.2.3(@types/react@19.2.16)(react@19.2.7): - dependencies: - get-nonce: 1.0.1 - react: 19.2.7 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.16 - - react-syntax-highlighter@16.1.1(react@19.2.7): - dependencies: - '@babel/runtime': 7.28.4 - highlight.js: 10.7.3 - highlightjs-vue: 1.0.0 - lowlight: 1.20.0 - prismjs: 1.30.0 - react: 19.2.7 - refractor: 5.0.0 - - react@19.2.7: {} - - reading-time@1.5.0: {} - - recharts@3.6.0(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react-is@16.13.1)(react@19.2.7)(redux@5.0.1): - dependencies: - '@reduxjs/toolkit': 2.12.0(react-redux@9.3.0(@types/react@19.2.16)(react@19.2.7)(redux@5.0.1))(react@19.2.7) - clsx: 2.1.1 - decimal.js-light: 2.5.1 - es-toolkit: 1.49.0 - eventemitter3: 5.0.4 - immer: 10.2.0 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - react-is: 16.13.1 - react-redux: 9.3.0(@types/react@19.2.16)(react@19.2.7)(redux@5.0.1) - reselect: 5.1.1 - tiny-invariant: 1.3.3 - use-sync-external-store: 1.6.0(react@19.2.7) - victory-vendor: 37.3.6 - transitivePeerDependencies: - - '@types/react' - - redux - - recma-build-jsx@1.0.0: - dependencies: - '@types/estree': 1.0.8 - estree-util-build-jsx: 3.0.1 - vfile: 6.0.3 - - recma-jsx@1.0.1(acorn@8.15.0): - dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.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.8 - estree-util-to-js: 2.0.0 - unified: 11.0.5 - vfile: 6.0.3 - - redux-immutable@4.0.0(immutable@4.3.9): - dependencies: - immutable: 4.3.9 - - redux-thunk@3.1.0(redux@5.0.1): - dependencies: - redux: 5.0.1 - - redux@5.0.1: {} - - refractor@5.0.0: - dependencies: - '@types/hast': 3.0.5 - '@types/prismjs': 1.26.5 - hastscript: 9.0.1 - parse-entities: 4.0.2 - - 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-harden@1.1.8: - dependencies: - unist-util-visit: 5.1.0 - - rehype-katex@7.0.1: - dependencies: - '@types/hast': 3.0.5 - '@types/katex': 0.16.7 - hast-util-from-html-isomorphic: 2.0.0 - hast-util-to-text: 4.0.2 - katex: 0.16.27 - unist-util-visit-parents: 6.0.2 - vfile: 6.0.3 - - rehype-parse@9.0.1: - dependencies: - '@types/hast': 3.0.5 - hast-util-from-html: 2.0.3 - unified: 11.0.5 - - rehype-pretty-code@0.14.1(shiki@3.20.0): - dependencies: - '@types/hast': 3.0.5 - hast-util-to-string: 3.0.1 - parse-numeric-range: 1.3.0 - rehype-parse: 9.0.1 - shiki: 3.20.0 - unified: 11.0.5 - unist-util-visit: 5.1.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.8 - '@types/hast': 3.0.5 - hast-util-to-estree: 3.1.3 - transitivePeerDependencies: - - supports-color - - rehype-sanitize@6.0.0: - dependencies: - '@types/hast': 3.0.5 - hast-util-sanitize: 5.0.2 - - remark-breaks@4.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-newline-to-break: 2.0.0 - unified: 11.0.5 - - remark-frontmatter@5.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-frontmatter: 2.0.1 - micromark-extension-frontmatter: 2.0.0 - unified: 11.0.5 - 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-math@6.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-math: 3.0.0 - micromark-extension-math: 3.1.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.2 - micromark-util-types: 2.0.2 - unified: 11.0.5 - transitivePeerDependencies: - - supports-color - - remark-reading-time@2.0.2: - dependencies: - estree-util-is-identifier-name: 2.1.0 - estree-util-value-to-estree: 3.5.0 - reading-time: 1.5.0 - unist-util-visit: 3.1.0 - - remark-rehype@11.1.2: - dependencies: - '@types/hast': 3.0.5 - '@types/mdast': 4.0.4 - mdast-util-to-hast: 13.2.1 - unified: 11.0.5 - vfile: 6.0.3 - - remark-smartypants@3.0.2: - dependencies: - retext: 9.0.0 - retext-smartypants: 6.2.0 - unified: 11.0.5 - unist-util-visit: 5.1.0 - - 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 - - remarkable@2.0.1: - dependencies: - argparse: 1.0.10 - autolinker: 3.16.2 - - remend@1.3.0: {} - - repeat-string@1.6.1: {} - - requires-port@1.0.0: {} - - reselect@5.1.1: {} - - reselect@5.2.0: {} - - restore-cursor@5.1.0: - dependencies: - onetime: 7.0.0 - signal-exit: 4.1.0 - - ret@0.2.2: {} - - retext-latin@4.0.0: - dependencies: - '@types/nlcst': 2.0.3 - parse-latin: 7.0.0 - unified: 11.0.5 - - retext-smartypants@6.2.0: - dependencies: - '@types/nlcst': 2.0.3 - nlcst-to-string: 4.0.0 - unist-util-visit: 5.1.0 - - retext-stringify@4.0.0: - dependencies: - '@types/nlcst': 2.0.3 - nlcst-to-string: 4.0.0 - unified: 11.0.5 - - retext@9.0.0: - dependencies: - '@types/nlcst': 2.0.3 - retext-latin: 4.0.0 - retext-stringify: 4.0.0 - unified: 11.0.5 - - reusify@1.1.0: {} - - rfdc@1.4.1: {} - - robust-predicates@3.0.2: {} - - rollup@4.62.2: - dependencies: - '@types/estree': 1.0.9 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.62.2 - '@rollup/rollup-android-arm64': 4.62.2 - '@rollup/rollup-darwin-arm64': 4.62.2 - '@rollup/rollup-darwin-x64': 4.62.2 - '@rollup/rollup-freebsd-arm64': 4.62.2 - '@rollup/rollup-freebsd-x64': 4.62.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.62.2 - '@rollup/rollup-linux-arm-musleabihf': 4.62.2 - '@rollup/rollup-linux-arm64-gnu': 4.62.2 - '@rollup/rollup-linux-arm64-musl': 4.62.2 - '@rollup/rollup-linux-loong64-gnu': 4.62.2 - '@rollup/rollup-linux-loong64-musl': 4.62.2 - '@rollup/rollup-linux-ppc64-gnu': 4.62.2 - '@rollup/rollup-linux-ppc64-musl': 4.62.2 - '@rollup/rollup-linux-riscv64-gnu': 4.62.2 - '@rollup/rollup-linux-riscv64-musl': 4.62.2 - '@rollup/rollup-linux-s390x-gnu': 4.62.2 - '@rollup/rollup-linux-x64-gnu': 4.62.2 - '@rollup/rollup-linux-x64-musl': 4.62.2 - '@rollup/rollup-openbsd-x64': 4.62.2 - '@rollup/rollup-openharmony-arm64': 4.62.2 - '@rollup/rollup-win32-arm64-msvc': 4.62.2 - '@rollup/rollup-win32-ia32-msvc': 4.62.2 - '@rollup/rollup-win32-x64-gnu': 4.62.2 - '@rollup/rollup-win32-x64-msvc': 4.62.2 - fsevents: 2.3.3 - - roughjs@4.6.6: - dependencies: - hachure-fill: 0.5.2 - path-data-parser: 0.1.0 - points-on-curve: 0.2.0 - points-on-path: 0.2.1 - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - rw@1.3.3: {} - - safe-buffer@5.2.1: {} - - safer-buffer@2.1.2: {} - - scheduler@0.27.0: {} - - scroll-into-view-if-needed@3.1.0: - dependencies: - compute-scroll-into-view: 3.1.1 - - search-insights@2.17.3: {} - - semver@7.8.5: - optional: true - - serialize-error@8.1.0: - dependencies: - type-fest: 0.20.2 - - seroval-plugins@1.6.8(seroval@1.6.8): - dependencies: - seroval: 1.6.8 - - seroval@1.6.8: {} - - server-only@0.0.1: {} - - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - - sha.js@2.4.12: - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - to-buffer: 1.2.2 - - sharp@0.35.5(@types/node@22.19.17): - dependencies: - '@img/colour': 1.1.0 - detect-libc: 2.1.2 - semver: 7.8.5 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.35.5 - '@img/sharp-darwin-x64': 0.35.5 - '@img/sharp-freebsd-wasm32': 0.35.5 - '@img/sharp-libvips-darwin-arm64': 1.3.4 - '@img/sharp-libvips-darwin-x64': 1.3.4 - '@img/sharp-libvips-linux-arm': 1.3.4 - '@img/sharp-libvips-linux-arm64': 1.3.4 - '@img/sharp-libvips-linux-ppc64': 1.3.4 - '@img/sharp-libvips-linux-riscv64': 1.3.4 - '@img/sharp-libvips-linux-s390x': 1.3.4 - '@img/sharp-libvips-linux-x64': 1.3.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 - '@img/sharp-libvips-linuxmusl-x64': 1.3.4 - '@img/sharp-linux-arm': 0.35.5 - '@img/sharp-linux-arm64': 0.35.5 - '@img/sharp-linux-ppc64': 0.35.5 - '@img/sharp-linux-riscv64': 0.35.5 - '@img/sharp-linux-s390x': 0.35.5 - '@img/sharp-linux-x64': 0.35.5 - '@img/sharp-linuxmusl-arm64': 0.35.5 - '@img/sharp-linuxmusl-x64': 0.35.5 - '@img/sharp-webcontainers-wasm32': 0.35.5 - '@img/sharp-win32-arm64': 0.35.5 - '@img/sharp-win32-ia32': 0.35.5 - '@img/sharp-win32-x64': 0.35.5 - '@types/node': 22.19.17 - optional: true - - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - shiki@3.20.0: - dependencies: - '@shikijs/core': 3.20.0 - '@shikijs/engine-javascript': 3.20.0 - '@shikijs/engine-oniguruma': 3.20.0 - '@shikijs/langs': 3.20.0 - '@shikijs/themes': 3.20.0 - '@shikijs/types': 3.20.0 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.5 - - shiki@4.1.0: - dependencies: - '@shikijs/core': 4.1.0 - '@shikijs/engine-javascript': 4.1.0 - '@shikijs/engine-oniguruma': 4.1.0 - '@shikijs/langs': 4.1.0 - '@shikijs/themes': 4.1.0 - '@shikijs/types': 4.1.0 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.5 - - short-unique-id@5.3.2: {} - - side-channel-list@1.0.1: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.1: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.1 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - - siginfo@2.0.0: {} - - signal-exit@4.1.0: {} - - sisteransi@1.0.5: {} - - slash@5.1.0: {} - - slice-ansi@7.1.2: - dependencies: - ansi-styles: 6.2.3 - is-fullwidth-code-point: 5.1.0 - - source-map-js@1.2.2: {} - - source-map@0.7.6: {} - - space-separated-tokens@2.0.2: {} - - speech-rule-engine@4.1.2: - dependencies: - '@xmldom/xmldom': 0.9.12 - commander: 13.1.0 - wicked-good-xpath: 1.3.0 - - sprintf-js@1.0.3: {} - - stackback@0.0.2: {} - - std-env@4.1.0: {} - - stdin-discarder@0.3.2: {} - - streamdown@2.5.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): - dependencies: - clsx: 2.1.1 - hast-util-to-jsx-runtime: 2.3.6 - html-url-attributes: 3.0.1 - marked: 17.0.3 - mermaid: 11.17.2 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - rehype-harden: 1.1.8 - rehype-raw: 7.0.0 - rehype-sanitize: 6.0.0 - remark-gfm: 4.0.1 - remark-parse: 11.0.0 - remark-rehype: 11.1.2 - remend: 1.3.0 - tailwind-merge: 3.6.0 - unified: 11.0.5 - unist-util-visit: 5.1.0 - unist-util-visit-parents: 6.0.2 - transitivePeerDependencies: - - supports-color - - strictdom@1.0.1: {} - - string-argv@0.3.2: {} - - string-width@7.2.0: - dependencies: - emoji-regex: 10.6.0 - get-east-asian-width: 1.4.0 - strip-ansi: 7.1.2 - - string-width@8.1.0: - dependencies: - get-east-asian-width: 1.4.0 - strip-ansi: 7.1.2 - - stringify-entities@4.0.4: - dependencies: - character-entities-html4: 2.1.0 - character-entities-legacy: 3.0.0 - - strip-ansi@7.1.2: - dependencies: - ansi-regex: 6.2.2 - - strip-final-newline@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.7): - dependencies: - client-only: 0.0.1 - react: 19.2.7 - - stylis@4.3.6: {} - - swagger-client@3.38.0: - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@scarf/scarf': 1.4.0 - '@swagger-api/apidom-core': 1.12.0 - '@swagger-api/apidom-error': 1.12.0 - '@swagger-api/apidom-json-pointer': 1.12.0 - '@swagger-api/apidom-ns-openapi-3-1': 1.12.0 - '@swagger-api/apidom-ns-openapi-3-2': 1.12.0 - '@swagger-api/apidom-reference': 1.12.0 - '@swaggerexpert/cookie': 2.0.2 - deepmerge: 4.3.1 - fast-json-patch: 3.1.1 - js-yaml: 4.3.2 - neotraverse: 1.0.1 - node-abort-controller: 3.1.1 - openapi-path-templating: 2.2.1 - openapi-server-url-templating: 1.3.0 - ramda: 0.30.1 - ramda-adjunct: 5.1.0(ramda@0.30.1) - optionalDependencies: - '@swagger-api/apidom-ns-asyncapi-2': 1.12.0 - '@swagger-api/apidom-ns-asyncapi-3': 1.12.0 - '@swagger-api/apidom-ns-openapi-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-api-design-systems-json': 1.12.0 - '@swagger-api/apidom-parser-adapter-api-design-systems-yaml': 1.12.0 - '@swagger-api/apidom-parser-adapter-arazzo-json-1': 1.12.0 - '@swagger-api/apidom-parser-adapter-arazzo-yaml-1': 1.12.0 - '@swagger-api/apidom-parser-adapter-asyncapi-json-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-asyncapi-json-3': 1.12.0 - '@swagger-api/apidom-parser-adapter-asyncapi-yaml-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-asyncapi-yaml-3': 1.12.0 - '@swagger-api/apidom-parser-adapter-json': 1.12.0 - '@swagger-api/apidom-parser-adapter-openapi-json-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-openapi-json-3-0': 1.12.0 - '@swagger-api/apidom-parser-adapter-openapi-json-3-1': 1.12.0 - '@swagger-api/apidom-parser-adapter-openapi-json-3-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-openapi-yaml-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-openapi-yaml-3-0': 1.12.0 - '@swagger-api/apidom-parser-adapter-openapi-yaml-3-1': 1.12.0 - '@swagger-api/apidom-parser-adapter-openapi-yaml-3-2': 1.12.0 - '@swagger-api/apidom-parser-adapter-yaml-1-2': 1.12.0 - transitivePeerDependencies: - - debug - - supports-color - - swagger-ui-react@5.32.14(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@scarf/scarf': 1.4.0 - base64-js: 1.5.1 - buffer: 6.0.3 - classnames: 2.5.1 - css.escape: 1.5.1 - deep-extend: 0.6.0 - dompurify: 3.4.14 - ieee754: 1.2.1 - immutable: 4.3.9 - js-file-download: 0.4.12 - js-yaml: 4.3.2 - lodash: 4.18.1 - prop-types: 15.8.1 - randexp: 0.5.3 - randombytes: 2.1.0 - react: 19.2.7 - react-copy-to-clipboard: 5.1.1(react@19.2.7) - react-debounce-input: 3.3.0(react@19.2.7) - react-dom: 19.2.7(react@19.2.7) - react-immutable-proptypes: 2.2.0(immutable@4.3.9) - react-immutable-pure-component: 2.2.2(immutable@4.3.9)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react-inspector: 6.0.2(react@19.2.7) - react-redux: 9.3.0(@types/react@19.2.16)(react@19.2.7)(redux@5.0.1) - react-syntax-highlighter: 16.1.1(react@19.2.7) - redux: 5.0.1 - redux-immutable: 4.0.0(immutable@4.3.9) - remarkable: 2.0.1 - reselect: 5.2.0 - serialize-error: 8.1.0 - sha.js: 2.4.12 - swagger-client: 3.38.0 - url-parse: 1.5.10 - xml: 1.0.1 - xml-but-prettier: 1.0.1 - zenscroll: 4.0.2 - transitivePeerDependencies: - - '@types/react' - - debug - - supports-color - - swr@2.4.2(react@19.2.7): - dependencies: - dequal: 2.0.3 - react: 19.2.7 - use-sync-external-store: 1.6.0(react@19.2.7) - - system-architecture@0.1.0: {} - - tabbable@6.3.0: {} - - tailwind-merge@3.6.0: {} - - tailwindcss-animate@1.0.7(tailwindcss@4.3.0): - dependencies: - tailwindcss: 4.3.0 - - tailwindcss@4.3.0: {} - - tapable@2.3.3: {} - - third-party-capital@1.0.20: {} - - throttleit@2.1.0: {} - - tiny-invariant@1.3.3: {} - - tinybench@2.9.0: {} - - tinyexec@1.1.1: {} - - tinyglobby@0.2.17: - dependencies: - fdir: 6.5.0(picomatch@4.0.5) - picomatch: 4.0.5 - - tinyrainbow@3.1.0: {} - - title@4.0.1: - dependencies: - arg: 5.0.2 - chalk: 5.6.2 - clipboardy: 4.0.0 - - to-buffer@1.2.2: - dependencies: - isarray: 2.0.5 - safe-buffer: 5.2.1 - typed-array-buffer: 1.0.3 - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - - toggle-selection@1.0.6: {} - - tree-sitter-json@0.24.8(tree-sitter@0.21.1): - dependencies: - node-addon-api: 8.5.0 - node-gyp-build: 4.8.4 - optionalDependencies: - tree-sitter: 0.21.1 - optional: true - - tree-sitter@0.21.1: - dependencies: - node-addon-api: 8.5.0 - node-gyp-build: 4.8.4 - optional: true - - tree-sitter@0.22.4: - dependencies: - node-addon-api: 8.5.0 - node-gyp-build: 4.8.4 - optional: true - - trim-lines@3.0.1: {} - - trough@2.2.0: {} - - trpc-cli@0.12.4(@trpc/server@11.16.0(typescript@5.9.3))(zod@4.3.6): - dependencies: - commander: 14.0.3 - optionalDependencies: - '@trpc/server': 11.16.0(typescript@5.9.3) - zod: 4.3.6 - - ts-algebra@2.0.0: {} - - ts-dedent@2.2.0: {} - - ts-mixer@6.0.4: {} - - ts-morph@27.0.2: - dependencies: - '@ts-morph/common': 0.28.1 - code-block-writer: 13.0.3 - - ts-toolbelt@9.6.0: {} - - tslib@2.8.1: {} - - tsx@4.23.8: - dependencies: - esbuild: 0.28.1 - optionalDependencies: - fsevents: 2.3.3 - - twoslash-protocol@0.3.4: {} - - twoslash@0.3.4(typescript@5.9.3): - dependencies: - '@typescript/vfs': 1.6.2(typescript@5.9.3) - twoslash-protocol: 0.3.4 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - type-fest@0.20.2: {} - - typed-array-buffer@1.0.3: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-typed-array: 1.1.15 - - types-ramda@0.30.1: - dependencies: - ts-toolbelt: 9.6.0 - - typescript@5.9.3: {} - - ufo@1.6.1: {} - - ultracite@6.1.0(typescript@5.9.3): - dependencies: - '@clack/prompts': 0.11.0 - '@trpc/server': 11.16.0(typescript@5.9.3) - deepmerge: 4.3.1 - jsonc-parser: 3.3.1 - nypm: 0.6.5 - trpc-cli: 0.12.4(@trpc/server@11.16.0(typescript@5.9.3))(zod@4.3.6) - zod: 4.3.6 - transitivePeerDependencies: - - '@orpc/server' - - '@valibot/to-json-schema' - - effect - - typescript - - valibot - - undici-types@6.21.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-find-after@5.0.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - - unist-util-is@5.2.1: - dependencies: - '@types/unist': 2.0.11 - - unist-util-is@6.0.1: - dependencies: - '@types/unist': 3.0.3 - - unist-util-modify-children@4.0.0: - dependencies: - '@types/unist': 3.0.3 - array-iterate: 2.0.1 - - unist-util-position-from-estree@2.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-position@5.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-remove-position@5.0.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-visit: 5.1.0 - - unist-util-remove@4.0.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - unist-util-visit-parents: 6.0.2 - - unist-util-stringify-position@4.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-visit-children@3.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-visit-parents@4.1.1: - dependencies: - '@types/unist': 2.0.11 - unist-util-is: 5.2.1 - - unist-util-visit-parents@6.0.2: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - - unist-util-visit@3.1.0: - dependencies: - '@types/unist': 2.0.11 - unist-util-is: 5.2.1 - unist-util-visit-parents: 4.1.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 - - universal-user-agent@7.0.3: {} - - unraw@3.0.0: {} - - url-parse@1.5.10: - dependencies: - querystringify: 2.2.0 - requires-port: 1.0.0 - - use-callback-ref@1.3.3(@types/react@19.2.16)(react@19.2.7): - dependencies: - react: 19.2.7 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.16 - - use-sidecar@1.1.3(@types/react@19.2.16)(react@19.2.7): - dependencies: - detect-node-es: 1.1.0 - react: 19.2.7 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.16 - - use-stick-to-bottom@1.1.6(react@19.2.7): - dependencies: - react: 19.2.7 - - use-sync-external-store@1.6.0(react@19.2.7): - dependencies: - react: 19.2.7 - - usehooks-ts@3.1.1(react@19.2.7): - dependencies: - lodash.debounce: 4.0.8 - react: 19.2.7 - - util-deprecate@1.0.2: {} - - uuid@11.1.1: {} - - 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 - - victory-vendor@37.3.6: - dependencies: - '@types/d3-array': 3.2.2 - '@types/d3-ease': 3.0.2 - '@types/d3-interpolate': 3.0.4 - '@types/d3-scale': 4.0.9 - '@types/d3-shape': 3.1.8 - '@types/d3-time': 3.0.4 - '@types/d3-timer': 3.0.2 - d3-array: 3.2.4 - d3-ease: 3.0.1 - d3-interpolate: 3.0.1 - d3-scale: 4.0.2 - d3-shape: 3.2.0 - d3-time: 3.1.0 - d3-timer: 3.0.1 - - vite@7.3.5(@types/node@22.19.17)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.23.8)(yaml@2.8.3): - dependencies: - esbuild: 0.28.1 - fdir: 6.5.0(picomatch@4.0.5) - picomatch: 4.0.5 - postcss: 8.5.26 - rollup: 4.62.2 - tinyglobby: 0.2.17 - optionalDependencies: - '@types/node': 22.19.17 - fsevents: 2.3.3 - jiti: 2.6.1 - lightningcss: 1.32.0 - tsx: 4.23.8 - yaml: 2.8.3 - - vitest@4.1.8(@opentelemetry/api@1.9.0)(@types/node@22.19.17)(vite@7.3.5(@types/node@22.19.17)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.23.8)(yaml@2.8.3)): - dependencies: - '@vitest/expect': 4.1.8 - '@vitest/mocker': 4.1.8(vite@7.3.5(@types/node@22.19.17)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.23.8)(yaml@2.8.3)) - '@vitest/pretty-format': 4.1.8 - '@vitest/runner': 4.1.8 - '@vitest/snapshot': 4.1.8 - '@vitest/spy': 4.1.8 - '@vitest/utils': 4.1.8 - es-module-lexer: 2.0.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.1.0 - tinybench: 2.9.0 - tinyexec: 1.1.1 - tinyglobby: 0.2.17 - tinyrainbow: 3.1.0 - vite: 7.3.5(@types/node@22.19.17)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.23.8)(yaml@2.8.3) - why-is-node-running: 2.3.0 - optionalDependencies: - '@opentelemetry/api': 1.9.0 - '@types/node': 22.19.17 - transitivePeerDependencies: - - msw - - web-namespaces@2.0.1: {} - - web-tree-sitter@0.24.5: - optional: true - - web-vitals@5.2.0: {} - - which-typed-array@1.1.19: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - for-each: 0.3.5 - get-proto: 1.0.1 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - why-is-node-running@2.3.0: - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 - - wicked-good-xpath@1.3.0: {} - - wrap-ansi@9.0.2: - dependencies: - ansi-styles: 6.2.3 - string-width: 7.2.0 - strip-ansi: 7.1.2 - - xml-but-prettier@1.0.1: - dependencies: - repeat-string: 1.6.1 - - xml@1.0.1: {} - - yaml@2.8.3: {} - - yoctocolors@2.1.2: {} - - zenscroll@4.0.2: {} - - zod@4.3.6: {} - - zustand@5.0.14(@types/react@19.2.16)(immer@11.1.15)(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7)): - optionalDependencies: - '@types/react': 19.2.16 - immer: 11.1.15 - react: 19.2.7 - use-sync-external-store: 1.6.0(react@19.2.7) - - zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml deleted file mode 100644 index 814a66ca7..000000000 --- a/pnpm-workspace.yaml +++ /dev/null @@ -1,61 +0,0 @@ -settings: - # Intended supply-chain guard: refuse versions published in the last 7 days (10080 minutes). - # packageManager is pnpm 10.11.0, which does not implement minimumReleaseAge (added in 10.16.0). - # This key documents the policy. It does not enforce it. Override pins are the control. - # This remediation does not upgrade pnpm. - minimumReleaseAge: 10080 - -overrides: - # Force next's transitive postcss to the same safe version as the direct dep in package.json. 8.5.23 fixes the sourceMappingURL arbitrary .map file read (GHSA-6g55-p6wh-862q follow-up). Pinned (not a range) because 8.5.27+ are still inside the 7-day minimumReleaseAge window above. - postcss: "8.5.26" - # rule-engine pulls a vulnerable @xmldom/xmldom (XML injection / uncontrolled recursion). 0.9.12 fixes the EntityReference.nodeName fragment injection. - "@xmldom/xmldom": ">=0.9.12" - # Force vite's transitive esbuild off the vulnerable dev-server file-read advisory (GHSA-g7r4-m6w7-qqqr) - esbuild: "0.28.1" - # Force form-data to a patched version across all transitive dependents (CVE-2026-12143, CRLF header injection) - form-data: "4.0.6" - # Force dompurify (via mermaid, posthog-js, swagger-ui-react) past mXSS advisories (CVE-2026-49459, CVE-2026-49458 fixed in 3.4.6; GHSA-76mc-f452-cxcm fixed in 3.4.7). Cap below 4 to stay within consumers' ^3 range. - dompurify: ">=3.4.13 <4" - # Force protobufjs (via @opentelemetry/otlp-transformer) past prototype-pollution advisory (CVE-2026-54269, fixed in 7.6.3). Cap below 8 to stay within otlp-transformer's ^7 range. - protobufjs: ">=7.6.3 <8" - # Force axios (via @ory/client, @swagger-api/apidom-reference) past the absolute-URL redirect advisory (GHSA-gcfj-64vw-6mp9, fixed in 1.18.0). Cap below 2 to stay within consumers' ^1 range. - axios: ">=1.19.0 <2" - # Force brace-expansion (via minimatch) past the recursion and quadratic-expansion advisories - # (CVE-2026-102276 fixed in 5.0.10, CVE-2026-102278 fixed in 5.0.11, CVE-2026-102277 fixed in 5.0.12). - # Cap below 6 to stay within minimatch's ^5 range. 5.0.9 still matches the two High ranges. - brace-expansion: ">=5.0.12 <6" - # Force seroval (via @tanstack/router-core) past CVE-2026-104846 (GHSA-p6vx-979v-rg4c, fixed in 1.6.2) - # and CVE-2026-104845 (GHSA-jp82-f5mq-hwhp, fixed in 1.6.3). 1.6.8 was selected manually. - # 1.6.9 (published 2026-10-09) is still inside the intended 7-day release-age window. - # pnpm 10.11.0 does not implement minimumReleaseAge, so the setting above would not hold 1.6.9 back. - # @tanstack/router-core@1.171.15 declares seroval ^1.5.4. - seroval: "1.6.8" - # Keep seroval-plugins on the same manually selected 1.6.8 release. router-core imports - # seroval-plugins/web and declares ^1.5.4. 1.6.9 is inside the intended 7-day window, and - # pnpm 10.11.0 would not age-gate a range, so this stays an exact pin. - seroval-plugins: "1.6.8" - # Force source-map-js (via postcss and @tailwindcss/node) past CVE-2026-93749 (GHSA-68fv-2mgg-jv7q, fixed in 1.2.2). - # Both parents declare ^1.2.1. 1.2.2 is the only published patched release. The exact pin is retained - # because pnpm 10.11.0 does not enforce minimumReleaseAge: a range would float to a newer publish - # the next time the lockfile is regenerated, including one still inside the intended 7-day window. - source-map-js: "1.2.2" - # Force sharp (optional dependency of next@16.3.8, range ^0.35.4) past GHSA-wq5f-xc86-pv6w - # (librsvg CVE-2026-96889, fixed in 0.35.5). 0.35.5 is the latest 0.35.x and stays inside Next's range. - # The exact pin is retained for the same reason as source-map-js: pnpm 10.11.0 will not age-gate a range. - # Leave next itself on 16.3.8. - sharp: "0.35.5" - # braces@3.0.3 (nextra and the direct fast-glob devDependency -> micromatch@4.0.8) has no published - # fix for GHSA-vfj7-8cjw-p6xm / CVE-2026-93687. The advisory range is <=3.0.3 and first_patched_version - # is unset. npm latest is still 3.0.3 (published 2024-05-21). Do not override to an unreleased fork. - # Force js-yaml (via next-validate-link, swagger-client, swagger-ui-react) past CVE-2026-59869 (fixed in 4.3.0). Cap below 5 to stay within consumers' ^4 range. - js-yaml: ">=4.3.1 <5" - # Force @opentelemetry/core (via posthog-js) past the unbounded W3C Baggage allocation advisory (CVE-2026-54285, fixed in 2.8.0). Capped below 2.11.0 because that release is still inside the 7-day minimumReleaseAge window above; raise the cap once it ages out. - "@opentelemetry/core": ">=2.8.0 <2.11.0" - # Force mermaid (via @theguild/remark-mermaid, streamdown) past the XY-chart and radar DoS loops, architecture/config prototype pollution, and sibling CSS injection (all fixed in 11.16.1). Cap below 12 to stay within consumers' ^11 range. - mermaid: ">=11.16.1 <12" - # Force nanoid (via postcss) past the infinite loops on zero and negative size (fixed in 3.3.18). Cap below 4 to stay within postcss's ^3 range. - nanoid: ">=3.3.18 <4" - # Force fflate (via posthog-js) past the unzipSync infinite loop on malformed ZIP64 archives (fixed in 0.4.9). Cap below 0.5 to stay within posthog-js's ^0.4.8 range. - fflate: ">=0.4.9 <0.5" - # Force qs (via instantsearch.js) past the array-limit bypass on bracket-key comma parsing (CVE-2026-82562) and the attacker-controlled isBuffer DoS (CVE-2026-82417), both fixed in 6.16.0. Cap below 7 to stay within instantsearch.js's ^6.5.1 range. - qs: ">=6.16.0 <7" diff --git a/proxy.ts b/proxy.ts index eeb2e2907..5de3ac723 100644 --- a/proxy.ts +++ b/proxy.ts @@ -140,6 +140,6 @@ export function proxy(request: NextRequest) { export const config = { matcher: [ - "/((?!api|_next/static|_next/image|favicon\\.ico|manifest|public|.*\\.svg$|.*\\.png$|.*\\.jpg$|.*\\.jpeg$|.*\\.gif$|.*\\.webp$|.*\\.ico$|.*\\.webmanifest$|.*\\.css$|.*\\.js$|.*\\.woff$|.*\\.woff2$|.*\\.ttf$|.*\\.eot$|.*\\.otf$|.*\\.pdf$|.*\\.txt$|.*\\.xml$|.*\\.json$|.*\\.py$|.*\\.mp4$).*)", + "/((?!api|llms\\.mdx|_next/static|_next/image|favicon\\.ico|manifest|public|.*\\.svg$|.*\\.png$|.*\\.jpg$|.*\\.jpeg$|.*\\.gif$|.*\\.webp$|.*\\.ico$|.*\\.webmanifest$|.*\\.css$|.*\\.js$|.*\\.woff$|.*\\.woff2$|.*\\.ttf$|.*\\.eot$|.*\\.otf$|.*\\.pdf$|.*\\.txt$|.*\\.xml$|.*\\.json$|.*\\.py$|.*\\.mp4$).*)", ], }; diff --git a/public/images/logo/.DS_Store b/public/images/logo/.DS_Store deleted file mode 100644 index ad72625e2..000000000 Binary files a/public/images/logo/.DS_Store and /dev/null differ diff --git a/scripts/check-meta-keys.ts b/scripts/check-meta-keys.ts index c71c8a67f..75936bc6c 100644 --- a/scripts/check-meta-keys.ts +++ b/scripts/check-meta-keys.ts @@ -1,21 +1,25 @@ -#!/usr/bin/env npx tsx +#!/usr/bin/env bun /** - * Validate that _meta.tsx keys match existing filesystem entries + * Validate that meta.json `pages` entries match existing filesystem entries * * Usage: - * pnpm check-meta [--staged-only] + * bun run check-meta [--staged-only] * * Features: - * - Scans all _meta.tsx files in app/en/ - * - Validates that each key corresponds to a sibling directory or page file - * - Skips special keys like "*", "index", "---", and external links - * - --staged-only: Only check staged _meta.tsx files (for pre-commit hook) + * - Scans all meta.json files in content/docs/ + * - Validates that each bare slug in `pages` corresponds to a sibling + * `<slug>.mdx`, `<slug>/index.mdx`, or `<slug>/` directory + * - Validates that every sibling page/folder is listed, unless the file + * declares a rest entry (`...` or `z...a`) + * - Skips separators (`---Title---`), links (`[Title](href)`), rest entries + * (`...`), and exclusions (`!slug`) + * - --staged-only: Only check staged meta.json files (for pre-commit hook) */ import { execSync } from "node:child_process"; -import { existsSync, readdirSync, readFileSync } from "node:fs"; -import { dirname } from "node:path"; +import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"; +import { dirname, join } from "node:path"; import fg from "fast-glob"; // Colors for terminal output @@ -31,31 +35,16 @@ const colors = { const args = process.argv.slice(2); const stagedOnly = args.includes("--staged-only"); -// Special keys that don't need filesystem entries -const SPECIAL_KEYS = new Set(["*", "index", "---"]); - -// Nested property names that should never be treated as page keys -const NESTED_PROPERTIES = new Set([ - "theme", - "title", - "display", - "href", - "newWindow", - "breadcrumb", - "toc", - "layout", - "copyPage", - "type", -]); - -// Top-level regex patterns -const META_OBJECT_REGEX = - /(?:const|export\s+const)\s+meta\s*(?::\s*\w+)?\s*=\s*\{/; -const KEY_CHAR_REGEX = /[a-zA-Z0-9_-]/; -const MDX_EXTENSION_REGEX = /\.mdx?$/; +const CONTENT_ROOT = "content/docs"; +const META_FILE_NAME = "meta.json"; -// Constants -const LOOK_BEHIND_LENGTH = 15; +// Entry forms that never map to a sibling page +const SEPARATOR_REGEX = /^---.*---$/; +const LINK_REGEX = /^\[.*\]\(.*\)$/; +const REST_REGEX = /^(\.\.\.|z\.\.\.a)$/; +const EXCLUDE_PREFIX = "!"; +const MDX_EXTENSION_REGEX = /\.mdx?$/; +const NON_SLUG_CHARS_REGEX = /[-_]/g; type MetaError = { file: string; @@ -63,18 +52,14 @@ type MetaError = { message: string; }; -type ParserState = { - keys: string[]; - depth: number; - index: number; - inString: boolean; - stringChar: string; - currentKey: string; - collectingKey: boolean; -}; +interface ParsedMeta { + excluded: Set<string>; + hasRest: boolean; + pages: string[]; +} /** - * Get list of staged _meta.tsx files + * Get list of staged meta.json files under content/docs */ function getStagedMetaFiles(): string[] { try { @@ -86,234 +71,102 @@ function getStagedMetaFiles(): string[] { ); return output .split("\n") - .filter((f) => f.endsWith("_meta.tsx") && f.startsWith("app/")); + .filter( + (f) => + f.endsWith(`/${META_FILE_NAME}`) && f.startsWith(`${CONTENT_ROOT}/`) + ); } catch { return []; } } /** - * Get all _meta.tsx files in app/en/ + * Get all meta.json files in content/docs/ */ function getAllMetaFiles(): string[] { - return fg.sync("app/en/**/_meta.tsx", { onlyFiles: true }); -} - -/** - * Simple key extractor - finds keys at depth 1 of the meta object - */ -function extractMetaKeys(content: string): string[] { - const match = META_OBJECT_REGEX.exec(content); - if (!match) { - return []; - } - - const startIndex = (match.index ?? 0) + match[0].length; - const state: ParserState = { - keys: [], - depth: 1, - index: startIndex, - inString: false, - stringChar: "", - currentKey: "", - collectingKey: false, - }; - - while (state.index < content.length && state.depth > 0) { - parseNextChar(content, state); - } - - return state.keys; -} - -/** - * Parse the next character in the content - */ -function parseNextChar(content: string, state: ParserState): void { - const char = content[state.index]; - const prevChar = state.index > 0 ? content[state.index - 1] : ""; - - if (handleStringBoundary(char, prevChar, state)) { - return; - } - - if (state.inString) { - handleStringContent(char, state); - return; - } - - if (char === "`") { - state.index = skipTemplateLiteral(content, state.index); - return; - } - - if (handleBraceDepth(char, state)) { - return; - } - - handleKeyParsing(content, char, state); - state.index += 1; -} - -/** - * Handle string boundary (quote characters) - */ -function handleStringBoundary( - char: string, - prevChar: string, - state: ParserState -): boolean { - if ((char !== '"' && char !== "'") || prevChar === "\\") { - return false; - } - - if (!state.inString) { - state.inString = true; - state.stringChar = char; - if (state.depth === 1) { - state.collectingKey = true; - state.currentKey = ""; - } - } else if (char === state.stringChar) { - state.inString = false; - state.collectingKey = false; - } - state.index += 1; - return true; -} - -/** - * Handle content inside a string - */ -function handleStringContent(char: string, state: ParserState): void { - if (state.collectingKey) { - state.currentKey += char; - } - state.index += 1; + return fg.sync(`${CONTENT_ROOT}/**/${META_FILE_NAME}`, { onlyFiles: true }); } /** - * Handle brace depth tracking + * Parse a meta.json file and extract its `pages` entries. + * Returns an error when the file is not valid JSON or `pages` is malformed. */ -function handleBraceDepth(char: string, state: ParserState): boolean { - if (char === "{") { - state.depth += 1; - state.index += 1; - return true; - } - - if (char === "}") { - state.depth -= 1; - state.index += 1; - return true; - } - - return false; -} - -/** - * Handle key parsing logic - */ -function handleKeyParsing( - content: string, - char: string, - state: ParserState -): void { - // Start collecting unquoted key - if (state.depth === 1 && KEY_CHAR_REGEX.test(char) && !state.collectingKey) { - const start = Math.max(0, state.index - LOOK_BEHIND_LENGTH); - const lookBehind = content.slice(start, state.index).trim(); - if (isKeyStart(lookBehind)) { - state.collectingKey = true; - state.currentKey = char; - } - return; - } - - // Continue collecting unquoted key - if (state.collectingKey && KEY_CHAR_REGEX.test(char)) { - state.currentKey += char; - return; +function parseMetaFile( + filePath: string +): { meta: ParsedMeta } | { error: MetaError } { + let parsed: unknown; + try { + parsed = JSON.parse(readFileSync(filePath, "utf-8")); + } catch (error) { + return { + error: { + file: filePath, + key: "", + message: `Invalid JSON: ${error instanceof Error ? error.message : String(error)}`, + }, + }; } - // Key ends at colon - if (char === ":" && state.depth === 1 && state.currentKey) { - addKeyIfValid(state.keys, state.currentKey); - state.currentKey = ""; - state.collectingKey = false; + if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) { + return { + error: { + file: filePath, + key: "", + message: "meta.json must be a JSON object", + }, + }; } - // Reset on comma - if (char === "," && state.depth === 1) { - state.currentKey = ""; - state.collectingKey = false; + const pagesValue = (parsed as { pages?: unknown }).pages; + if (pagesValue === undefined) { + return { meta: { excluded: new Set(), hasRest: true, pages: [] } }; } -} -/** - * Skip over a template literal starting at index i - */ -function skipTemplateLiteral(content: string, startIndex: number): number { - let i = startIndex + 1; - while (i < content.length && content[i] !== "`") { - if (content[i] === "\\" && i + 1 < content.length) { - i += 1; - } - i += 1; + if ( + !Array.isArray(pagesValue) || + pagesValue.some((entry) => typeof entry !== "string") + ) { + return { + error: { + file: filePath, + key: "pages", + message: '"pages" must be an array of strings', + }, + }; } - return i + 1; -} -/** - * Check if the lookBehind indicates start of a new key - */ -function isKeyStart(lookBehind: string): boolean { - return ( - lookBehind.endsWith("{") || - lookBehind.endsWith(",") || - lookBehind.endsWith("\n") + const pages = pagesValue as string[]; + const excluded = new Set( + pages + .filter((entry) => entry.startsWith(EXCLUDE_PREFIX)) + .map((entry) => entry.slice(EXCLUDE_PREFIX.length)) ); -} - -/** - * Add key to list if it's valid (not special or nested property) - */ -function addKeyIfValid(keys: string[], key: string): void { - if (!(SPECIAL_KEYS.has(key) || NESTED_PROPERTIES.has(key))) { - keys.push(key); - } -} -/** - * Check if a key has an href property (making it an external link) - */ -function keyHasHref(content: string, key: string): boolean { - const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); - const pattern = new RegExp( - `["']?${escapedKey}["']?\\s*:\\s*\\{[^}]*href\\s*:`, - "s" - ); - return pattern.test(content); + return { + meta: { + excluded, + hasRest: pages.some((entry) => REST_REGEX.test(entry)), + pages, + }, + }; } /** - * Check if a key is a separator (type: "separator" or starts with --) + * True when the entry is a separator, link, rest marker, or exclusion — + * none of which must match a sibling page. */ -function keyIsSeparator(content: string, key: string): boolean { - if (key.startsWith("--")) { - return true; - } - - const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); - const pattern = new RegExp( - `["']?${escapedKey}["']?\\s*:\\s*\\{[^}]*type\\s*:\\s*["']separator["']`, - "s" +function isSpecialEntry(entry: string): boolean { + return ( + SEPARATOR_REGEX.test(entry) || + LINK_REGEX.test(entry) || + REST_REGEX.test(entry) || + entry.startsWith(EXCLUDE_PREFIX) ); - return pattern.test(content); } /** - * Get valid sibling names for a _meta.tsx file + * Get valid sibling names for a meta.json file: every `<name>.mdx`/`.md` file + * (including `index`) and every directory next to it. */ function getValidSiblings(metaFilePath: string): Set<string> { const dir = dirname(metaFilePath); @@ -323,7 +176,7 @@ function getValidSiblings(metaFilePath: string): Set<string> { const entries = readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { - if (entry.name === "_meta.tsx") { + if (entry.name === META_FILE_NAME || entry.name.startsWith(".")) { continue; } @@ -332,11 +185,7 @@ function getValidSiblings(metaFilePath: string): Set<string> { continue; } - const isMarkdown = - entry.name.endsWith(".mdx") || entry.name.endsWith(".md"); - const isPage = entry.name === "page.mdx" || entry.name === "page.md"; - - if (isMarkdown && !isPage) { + if (MDX_EXTENSION_REGEX.test(entry.name)) { siblings.add(entry.name.replace(MDX_EXTENSION_REGEX, "")); } } @@ -348,34 +197,116 @@ function getValidSiblings(metaFilePath: string): Set<string> { } /** - * Validate a single _meta.tsx file + * Resolve a path-like entry (containing "/") relative to the meta.json dir. + * Fumadocs accepts `folder/page` and `../other/page` forms. */ -function validateMetaFile(filePath: string): MetaError[] { - const errors: MetaError[] = []; +function pathEntryExists(metaFilePath: string, entry: string): boolean { + const base = join(dirname(metaFilePath), entry); + if (existsSync(`${base}.mdx`) || existsSync(`${base}.md`)) { + return true; + } + if (existsSync(base)) { + try { + return statSync(base).isDirectory(); + } catch { + return false; + } + } + return false; +} + +/** + * Validate one bare or path-like `pages` entry against the siblings on disk. + * Returns the error to report, or null when the entry resolves. + */ +function validateEntry( + filePath: string, + entry: string, + validSiblings: Set<string> +): MetaError | null { + if (entry.includes("/")) { + if (pathEntryExists(filePath, entry)) { + return null; + } + return { + file: filePath, + key: entry, + message: `Entry "${entry}" does not resolve to a page or folder relative to ${dirname(filePath)}`, + }; + } + if (validSiblings.has(entry)) { + return null; + } + + const suggestions = findSimilarSiblings(entry, validSiblings); + let message = `Entry "${entry}" does not match any sibling page or folder`; + if (suggestions.length > 0) { + message += `. Did you mean: ${suggestions.map((s) => `"${s}"`).join(", ")}?`; + } + return { file: filePath, key: entry, message }; +} + +/** + * Report every sibling page/folder that `pages` neither lists nor excludes. + * `index` is the folder's own page; Fumadocs attaches it to the folder node, + * so it does not need to be listed. + */ +function findUnlistedSiblings( + filePath: string, + validSiblings: Set<string>, + listed: Set<string>, + excluded: Set<string> +): MetaError[] { + return [...validSiblings] + .filter( + (sibling) => + sibling !== "index" && !(listed.has(sibling) || excluded.has(sibling)) + ) + .map((sibling) => ({ + file: filePath, + key: sibling, + message: `Sibling "${sibling}" is not listed in "pages" (add it, exclude it with "!${sibling}", or add "..." to include the rest)`, + })); +} + +/** + * Validate a single meta.json file + */ +function validateMetaFile(filePath: string): MetaError[] { if (!existsSync(filePath)) { - return errors; + return []; + } + + const result = parseMetaFile(filePath); + if ("error" in result) { + return [result.error]; } - const content = readFileSync(filePath, "utf-8"); - const keys = extractMetaKeys(content); + const { pages, hasRest, excluded } = result.meta; const validSiblings = getValidSiblings(filePath); + const errors: MetaError[] = []; + const listed = new Set<string>(); - for (const key of keys) { - if (keyHasHref(content, key) || keyIsSeparator(content, key)) { + for (const entry of pages) { + if (isSpecialEntry(entry)) { continue; } - - if (!validSiblings.has(key)) { - const suggestions = findSimilarSiblings(key, validSiblings); - let message = `Key "${key}" does not match any sibling directory or file`; - if (suggestions.length > 0) { - message += `. Did you mean: ${suggestions.map((s) => `"${s}"`).join(", ")}?`; - } - errors.push({ file: filePath, key, message }); + if (!entry.includes("/")) { + listed.add(entry); + } + const error = validateEntry(filePath, entry, validSiblings); + if (error) { + errors.push(error); } } + if (!hasRest) { + errors.push( + ...findUnlistedSiblings(filePath, validSiblings, listed, excluded) + ); + } + return errors; } @@ -386,9 +317,9 @@ function findSimilarSiblings( key: string, validSiblings: Set<string> ): string[] { - const normalizedKey = key.replace(/[-_]/g, ""); + const normalizedKey = key.replace(NON_SLUG_CHARS_REGEX, "").toLowerCase(); return [...validSiblings].filter((s) => { - const normalizedSibling = s.replace(/[-_]/g, ""); + const normalizedSibling = s.replace(NON_SLUG_CHARS_REGEX, "").toLowerCase(); return normalizedKey === normalizedSibling; }); } @@ -397,21 +328,21 @@ function findSimilarSiblings( * Main function */ function main(): void { - console.log(colors.blue("🔍 Checking _meta.tsx keys...\n")); + console.log(colors.blue("🔍 Checking meta.json pages...\n")); const metaFiles = stagedOnly ? getStagedMetaFiles() : getAllMetaFiles(); if (metaFiles.length === 0) { if (stagedOnly) { - console.log(colors.dim("No staged _meta.tsx files to check")); + console.log(colors.dim("No staged meta.json files to check")); } else { - console.log(colors.yellow("No _meta.tsx files found")); + console.log(colors.yellow("No meta.json files found")); } process.exit(0); } console.log( - colors.dim(`Checking ${metaFiles.length} _meta.tsx file(s)...\n`) + colors.dim(`Checking ${metaFiles.length} meta.json file(s)...\n`) ); const allErrors: MetaError[] = []; @@ -422,11 +353,11 @@ function main(): void { } if (allErrors.length === 0) { - console.log(colors.green("✅ All _meta.tsx keys are valid\n")); + console.log(colors.green("✅ All meta.json entries are valid\n")); process.exit(0); } - console.log(colors.red(`❌ Found ${allErrors.length} invalid key(s):\n`)); + console.log(colors.red(`❌ Found ${allErrors.length} problem(s):\n`)); for (const error of allErrors) { console.log(colors.yellow(` ${error.file}:`)); @@ -435,10 +366,19 @@ function main(): void { console.log( colors.dim( - "Meta keys must match sibling directories or page files (without extension)." + "Bare entries in `pages` must match a sibling `<slug>.mdx`, `<slug>/index.mdx`, or `<slug>/` folder," + ) + ); + console.log( + colors.dim( + "and every sibling must be listed unless the file contains a `...` rest entry." + ) + ); + console.log( + colors.dim( + "Separators (`---Title---`), links (`[Title](href)`), and exclusions (`!slug`) are allowed.\n" ) ); - console.log(colors.dim("Special keys like *, index, and --- are allowed.\n")); process.exit(1); } diff --git a/scripts/check-redirects.ts b/scripts/check-redirects.ts index 23733e0c3..0b773f1ec 100644 --- a/scripts/check-redirects.ts +++ b/scripts/check-redirects.ts @@ -1,10 +1,10 @@ -#!/usr/bin/env npx tsx +#!/usr/bin/env bun /** * Check that deleted/renamed markdown files have corresponding redirects in redirects.ts * * Usage: - * pnpm check-redirects [--auto-fix] [--staged-only] [base_branch] + * bun run check-redirects [--auto-fix] [--staged-only] [base_branch] * * Features: * - Detects deleted AND renamed markdown files without redirects @@ -18,6 +18,7 @@ import { execSync } from "node:child_process"; import { readFileSync, writeFileSync } from "node:fs"; import { redirects as configuredRedirects } from "../redirects"; import { + CONTENT_FILE_REGEX, checkWildcardMatch, type DynamicRouteMove, dynamicRouteExists, @@ -45,7 +46,6 @@ const baseBranch = args.find((arg) => !arg.startsWith("--")) || "main"; const REDIRECTS_FILE = "redirects.ts"; // Top-level regex patterns for performance -const PAGE_FILE_MATCH_REGEX = /page\.mdx?$/; const LOCALE_PATH_PREFIX_REGEX = /^\/:locale\//; const SPECIAL_REGEX_CHARS_REGEX = /[.*+?^${}()|[\]\\]/g; @@ -67,16 +67,14 @@ function parseGitDiffOutput( if (!line) { continue; } - const parts = line.split("\t"); - const status = parts[0]; - const filePath = parts[1]; + const [status, filePath] = line.split("\t"); - if (status === "D" && filePath && PAGE_FILE_MATCH_REGEX.test(filePath)) { + if (status === "D" && filePath && CONTENT_FILE_REGEX.test(filePath)) { deletedFiles.push(filePath); } else if ( status?.startsWith("R") && filePath && - PAGE_FILE_MATCH_REGEX.test(filePath) + CONTENT_FILE_REGEX.test(filePath) ) { renamedFromFiles.push(filePath); } @@ -223,11 +221,8 @@ function findFinalRedirectDestination( const visited = new Set<string>(); let current = path; - while (true) { - // Prevent infinite loops from circular redirects - if (visited.has(current)) { - return null; - } + // Stop when a path repeats: that is a circular redirect, not a destination. + while (!visited.has(current)) { visited.add(current); const redirect = redirectList.find((r) => r.source === current); @@ -238,6 +233,8 @@ function findFinalRedirectDestination( current = redirect.destination; } + + return null; } /** @@ -408,7 +405,7 @@ const { deleted, renamedFrom } = getDeletedAndRenamedFiles( stagedOnly ); const allDeletedOrRenamed = [...deleted, ...renamedFrom].filter((f) => - f.startsWith("app/") + CONTENT_FILE_REGEX.test(f) ); if (allDeletedOrRenamed.length === 0) { @@ -435,8 +432,12 @@ for (const file of allDeletedOrRenamed) { const hasExactRedirect = latestRedirects.some((r) => r.source === urlPath); const hasWildcardRedirect = checkWildcardMatch(urlPath, latestRedirects); const servedByDynamicRoute = dynamicRouteExists(urlPath); + // A rename between `<slug>.mdx` and `<slug>/index.mdx` keeps the URL. + const stillServedByFile = pageExists(urlPath); - if (hasExactRedirect) { + if (stillServedByFile) { + console.log(colors.green(`✓ URL still served by a page file: ${urlPath}`)); + } else if (hasExactRedirect) { console.log(colors.green(`✓ Redirect exists for: ${urlPath}`)); } else if (hasWildcardRedirect) { console.log( @@ -450,10 +451,11 @@ for (const file of allDeletedOrRenamed) { console.log(colors.red(`✗ Missing redirect for: ${urlPath}`)); missingRedirects.push(urlPath); + // Keys sorted to match the Biome `useSortedKeys` assist applied to redirects.ts. suggestedEntries.push(` { - source: "${urlPath}", destination: "/:locale/REPLACE_WITH_NEW_PATH", permanent: true, + source: "${urlPath}", },`); exitCode = 1; @@ -536,7 +538,7 @@ if (missingRedirects.length > 0) { ); console.log(""); console.log( - "When you delete a markdown file, you must add a redirect in redirects.ts" + "When you delete a content/docs page, you must add a redirect in redirects.ts" ); console.log( "to prevent broken links for users who have bookmarked the old URL." @@ -580,7 +582,7 @@ if (invalidRedirects.length > 0) { console.log(" 1. Open redirects.ts"); console.log(" 2. Find the redirect(s) listed above"); console.log(" 3. Update the destination to a valid page path"); - console.log(" (Check that the path exists under app/en/)"); + console.log(" (Check that the path exists under content/docs/)"); } // ============================================================ @@ -619,9 +621,9 @@ if (uncoveredMoves.length > 0) { console.log(""); console.log(colors.yellow(" Suggested redirect:")); console.log(` { - source: "${move.oldUrl}/:path*", destination: "${move.newUrl}/:path*", permanent: true, + source: "${move.oldUrl}/:path*", },`); console.log(""); } diff --git a/scripts/generate-llmstxt.ts b/scripts/generate-llmstxt.ts index 1224747fa..2bd49f890 100644 --- a/scripts/generate-llmstxt.ts +++ b/scripts/generate-llmstxt.ts @@ -1,10 +1,11 @@ import { execSync } from "node:child_process"; import fs from "node:fs/promises"; import path from "node:path"; +import { pathToFileURL } from "node:url"; +import Anthropic from "@anthropic-ai/sdk"; import chalk from "chalk"; import glob from "fast-glob"; -import Anthropic from "@anthropic-ai/sdk"; -import { getToolkitCanonicalPath } from "../app/_lib/toolkit-static-params"; +import { getToolkitCanonicalPath } from "../lib/toolkit-static-params"; import { resolveToolkitDataDir } from "../toolkit-docs-generator/src/shared/toolkit-data-dir"; import type { MergedToolkit, @@ -43,14 +44,15 @@ const BASE_URL = "https://docs.arcade.dev"; const OUTPUT_PATH = path.join(process.cwd(), "public", "llms.txt"); // Regex patterns used in path processing -const APP_EN_PREFIX_REGEX = /^app\/en\//; -const PAGE_MDX_SUFFIX_REGEX = /\/page\.mdx$/; +const CONTENT_ROOT = "content/docs"; +const CONTENT_PREFIX_REGEX = /^content\/docs\//; +// `content/docs/a/b/index.mdx` and `content/docs/a/b.mdx` both map to /en/a/b. +const INDEX_FILE_SUFFIX_REGEX = /(^|\/)index\.mdx$/; const MDX_SUFFIX_REGEX = /\.mdx$/; -// Matches a Next.js dynamic route segment, e.g. "[toolkitId]". -const DYNAMIC_SEGMENT_REGEX = /\[[^/]+\]/; const TITLE_H1_REGEX = /^#\s+(.+)$/m; const FRONTMATTER_REGEX = /^---\n([\s\S]*?)\n---/; const FRONTMATTER_TITLE_REGEX = /^title:\s*(.+)$/m; +const QUOTED_VALUE_REGEX = /^['"](.*)['"]$/; const EN_LOCALE_PREFIX_REGEX = /^en\//; const METADATA_REGEX = /^<!--\s*git-sha:\s*([^\s]+)\s+generation-date:\s*([^\s]+)\s*-->/; @@ -90,11 +92,11 @@ const anthropic = new Anthropic({ function getCurrentGitSha(): string { try { return execSync("git rev-parse HEAD", { encoding: "utf-8" }).trim(); - } catch (_error) { + } catch (error) { console.error( chalk.red("✗ Could not get git SHA. Make sure you're in a git repository.") ); - throw new Error("Failed to get git SHA"); + throw new Error("Failed to get git SHA", { cause: error }); } } @@ -166,13 +168,8 @@ async function extractExistingSummaries(): Promise< try { const content = await fs.readFile(OUTPUT_PATH, "utf-8"); // Match markdown links with descriptions: - [title](url): description - let match: RegExpExecArray | null; - // biome-ignore lint/suspicious/noAssignInExpressions: needed for regex.exec loop - while ((match = LINK_REGEX.exec(content)) !== null) { - const title = match[1]; - const url = match[2]; - const description = match[3].trim(); - summaries.set(url, { title, description }); + for (const [, title, url, rawDescription] of content.matchAll(LINK_REGEX)) { + summaries.set(url, { description: rawDescription.trim(), title }); } } catch (_error) { // File doesn't exist or can't be read - that's okay @@ -183,23 +180,34 @@ async function extractExistingSummaries(): Promise< /** * Discovers all pages in the documentation */ -async function discoverPages(): Promise<PageMetadata[]> { +function discoverPages(): Promise<PageMetadata[]> { console.log(chalk.blue("📄 Discovering pages from raw MDX...")); return discoverMdxPages(); } /** - * Discovers pages from raw MDX files (fallback) + * Converts a content file path to its public URL. + * e.g. content/docs/build/tools.mdx -> https://docs.arcade.dev/en/build/tools + * content/docs/build/tools/index.mdx -> https://docs.arcade.dev/en/build/tools + * content/docs/index.mdx -> https://docs.arcade.dev/en + */ +export function contentFileToUrl(filePath: string): string { + const relativePath = filePath + .replace(CONTENT_PREFIX_REGEX, "") + .replace(INDEX_FILE_SUFFIX_REGEX, "") + .replace(MDX_SUFFIX_REGEX, ""); + + return relativePath ? `${BASE_URL}/en/${relativePath}` : `${BASE_URL}/en`; +} + +/** + * Discovers pages from raw MDX files under content/docs */ async function discoverMdxPages(): Promise<PageMetadata[]> { - const mdxFiles = glob - .sync("app/en/**/*.mdx", { - cwd: process.cwd(), - ignore: ["**/node_modules/**", "**/_*.mdx"], - }) - // Skip dynamic-route templates (e.g. ".../[toolkitId]/page.mdx"): their - // path isn't a real URL, so they'd produce dead "[toolkitId]" links. - .filter((filePath) => !DYNAMIC_SEGMENT_REGEX.test(filePath)); + const mdxFiles = glob.sync(`${CONTENT_ROOT}/**/*.mdx`, { + cwd: process.cwd(), + ignore: ["**/node_modules/**", "**/_*.mdx"], + }); const pages: PageMetadata[] = []; @@ -207,13 +215,7 @@ async function discoverMdxPages(): Promise<PageMetadata[]> { const fullPath = path.join(process.cwd(), filePath); const content = await fs.readFile(fullPath, "utf-8"); - // Convert file path to URL - const relativePath = filePath - .replace(APP_EN_PREFIX_REGEX, "") - .replace(PAGE_MDX_SUFFIX_REGEX, "") - .replace(MDX_SUFFIX_REGEX, ""); - - const url = `${BASE_URL}/en/${relativePath}`; + const url = contentFileToUrl(filePath); pages.push({ path: filePath, @@ -500,29 +502,34 @@ function formatSectionName(segment: string): string { /** * Derives a readable title from a page's file path, used when the MDX has no - * H1. For ".../<slug>/page.mdx" this title-cases the parent directory - * (e.g. "examples" -> "Examples") instead of the literal filename "page". + * title. For ".../<slug>/index.mdx" this title-cases the parent directory + * (e.g. "examples" -> "Examples") instead of the literal filename "index". */ function deriveTitleFromPath(filePath: string): string { const base = path.basename(filePath, ".mdx"); - const slug = base === "page" ? path.basename(path.dirname(filePath)) : base; + const slug = base === "index" ? path.basename(path.dirname(filePath)) : base; return formatSectionName(slug); } /** - * Resolves a page title from MDX content: H1 > frontmatter title > path fallback. + * Resolves a page title from MDX content: frontmatter title > H1 > path + * fallback. Fumadocs renders the frontmatter `title` as the page heading, so + * it is the canonical source; an H1 only remains in legacy bodies. */ -function extractPageTitle(content: string, filePath: string): string { - const titleMatch = content.match(TITLE_H1_REGEX); - if (titleMatch) { - return titleMatch[1].trim(); - } +export function extractPageTitle(content: string, filePath: string): string { const frontmatterTitle = content .match(FRONTMATTER_REGEX)?.[1] ?.match(FRONTMATTER_TITLE_REGEX)?.[1] ?.trim() - ?.replace(/^['"](.*)['"]$/, "$1"); - return frontmatterTitle ?? deriveTitleFromPath(filePath); + ?.replace(QUOTED_VALUE_REGEX, "$1"); + if (frontmatterTitle) { + return frontmatterTitle; + } + const titleMatch = content.match(TITLE_H1_REGEX); + if (titleMatch) { + return titleMatch[1].trim(); + } + return deriveTitleFromPath(filePath); } /** @@ -599,11 +606,80 @@ function generateLlmsTxt( return lines.join("\n"); } +type SummarizedPage = PageMetadata & { title: string; description: string }; +type SummaryMap = Map<string, { title: string; description: string }>; + /** - * True when two llms.txt contents differ at most in their git-sha/date header + * Splits MDX pages into those whose summary must be regenerated (new or + * changed since the last run) and those whose existing summary can be kept. + * Repairs the stale "page"/"index" fallback title that older generations + * produced for pages without a title. */ -function hasSameBody(a: string, b: string): boolean { - return a.replace(METADATA_REGEX, "") === b.replace(METADATA_REGEX, ""); +function partitionMdxPages( + pages: PageMetadata[], + changedFiles: Set<string>, + existingSummaries: SummaryMap +): { + pagesToSummarize: PageMetadata[]; + pagesToKeep: SummarizedPage[]; + repairedTitles: number; +} { + const pagesToSummarize: PageMetadata[] = []; + const pagesToKeep: SummarizedPage[] = []; + let repairedTitles = 0; + + for (const page of pages) { + const { url } = page; + const existingSummary = existingSummaries.get(url); + + if ( + changedFiles.has(page.path) || + !existingSummary || + existingSummary.description === PLACEHOLDER_DESCRIPTION + ) { + pagesToSummarize.push(page); + continue; + } + + const title = + existingSummary.title === "page" || existingSummary.title === "index" + ? extractPageTitle(page.content, page.path) + : existingSummary.title; + if (title !== existingSummary.title) { + repairedTitles += 1; + } + pagesToKeep.push({ + ...page, + description: existingSummary.description, + title, + }); + } + + return { pagesToKeep, pagesToSummarize, repairedTitles }; +} + +/** + * Toolkit pages are rebuilt every run (no Claude). A change is any toolkit + * page that is new or whose title/description differs from the previous + * output. URLs already covered by MDX pages are skipped: those win at merge + * and their summaries are tracked by `partitionMdxPages`. + */ +function toolkitPagesChanged( + toolkitPages: SummarizedPage[], + mdxPageUrls: Set<string>, + existingSummaries: SummaryMap +): boolean { + return toolkitPages.some((toolkitPage) => { + if (mdxPageUrls.has(toolkitPage.url)) { + return false; + } + const existing = existingSummaries.get(toolkitPage.url); + return ( + !existing || + existing.title !== toolkitPage.title || + existing.description !== toolkitPage.description + ); + }); } /** @@ -612,118 +688,70 @@ function hasSameBody(a: string, b: string): boolean { function determinePagesToSummarize( pages: PageMetadata[], previousMetadata: LlmsTxtMetadata | null, - existingSummaries: Map<string, { title: string; description: string }>, - toolkitPages: Array<PageMetadata & { title: string; description: string }> + existingSummaries: SummaryMap, + toolkitPages: SummarizedPage[] ): { pagesToSummarize: PageMetadata[]; - pagesToKeep: Array<PageMetadata & { title: string; description: string }>; + pagesToKeep: SummarizedPage[]; hasChanges: boolean; } { - const pagesToSummarize: PageMetadata[] = []; - const pagesToKeep: Array< - PageMetadata & { title: string; description: string } - > = []; - let hasChanges = false; - - if (previousMetadata && previousMetadata.gitSha !== "unknown") { - // Get changed files since last generation - const changedFiles = getChangedFilesSince(previousMetadata.gitSha); + if (!previousMetadata || previousMetadata.gitSha === "unknown") { + // No previous generation or can't determine, summarize all pages console.log( - chalk.blue( - `\n📊 Found ${changedFiles.size} changed files since last generation` - ) - ); - - // MDX-authored pages take precedence over toolkit JSON at merge time. - const mdxPageUrls = new Set(pages.map((page) => page.url)); - - // Create a set of current page URLs for quick lookup. Include toolkit pages - // so they aren't mistaken for deleted pages (they aren't in `pages`). - const currentPageUrls = new Set( - [...pages, ...toolkitPages].map((page) => page.url) + chalk.yellow("⚠ No previous generation found, summarizing all pages") ); + return { hasChanges: true, pagesToKeep: [], pagesToSummarize: [...pages] }; + } - // Identify deleted pages (pages that exist in previous llms.txt but not in current filesystem) - const deletedPageUrls = Array.from(existingSummaries.keys()).filter( - (url) => !currentPageUrls.has(url) - ); + // Get changed files since last generation + const changedFiles = getChangedFilesSince(previousMetadata.gitSha); + console.log( + chalk.blue( + `\n📊 Found ${changedFiles.size} changed files since last generation` + ) + ); - if (deletedPageUrls.length > 0) { - hasChanges = true; - console.log( - chalk.yellow( - `\n🗑️ Found ${deletedPageUrls.length} deleted pages (will be removed from output)` - ) - ); - } + // MDX-authored pages take precedence over toolkit JSON at merge time. + const mdxPageUrls = new Set(pages.map((page) => page.url)); - // Filter pages based on changes - for (const page of pages) { - const url = page.url; - const existingSummary = existingSummaries.get(url); - - // Check if this page's file was changed - const isChanged = changedFiles.has(page.path); - - if ( - isChanged || - !existingSummary || - existingSummary.description === PLACEHOLDER_DESCRIPTION - ) { - // Need to summarize this page - pagesToSummarize.push(page); - hasChanges = true; - } else { - // Keep existing summary, but repair the stale "page" fallback title - // that older generations produced for pages without an H1. - const title = - existingSummary.title === "page" - ? extractPageTitle(page.content, page.path) - : existingSummary.title; - if (title !== existingSummary.title) { - hasChanges = true; - } - pagesToKeep.push({ - ...page, - title, - description: existingSummary.description, - }); - } - } + // Create a set of current page URLs for quick lookup. Include toolkit pages + // so they aren't mistaken for deleted pages (they aren't in `pages`). + const currentPageUrls = new Set( + [...pages, ...toolkitPages].map((page) => page.url) + ); - // Toolkit pages are rebuilt every run (no Claude). Flag a change if any is - // new or its title/description differs from the previous output. Skip URLs - // already covered by MDX pages — those win at merge and their summaries - // are tracked in the MDX loop above. - for (const toolkitPage of toolkitPages) { - if (mdxPageUrls.has(toolkitPage.url)) { - continue; - } - const existing = existingSummaries.get(toolkitPage.url); - if ( - !existing || - existing.title !== toolkitPage.title || - existing.description !== toolkitPage.description - ) { - hasChanges = true; - } - } + // Identify deleted pages (pages that exist in previous llms.txt but not in current filesystem) + const deletedPageUrls = Array.from(existingSummaries.keys()).filter( + (url) => !currentPageUrls.has(url) + ); + if (deletedPageUrls.length > 0) { console.log( - chalk.green( - `✓ ${pagesToKeep.length} pages unchanged, ${pagesToSummarize.length} pages to summarize${deletedPageUrls.length > 0 ? `, ${deletedPageUrls.length} pages deleted` : ""}` + chalk.yellow( + `\n🗑️ Found ${deletedPageUrls.length} deleted pages (will be removed from output)` ) ); - } else { - // No previous generation or can't determine, summarize all pages - console.log( - chalk.yellow("⚠ No previous generation found, summarizing all pages") - ); - pagesToSummarize.push(...pages); - hasChanges = true; // Always regenerate if no previous metadata } - return { pagesToSummarize, pagesToKeep, hasChanges }; + const { pagesToSummarize, pagesToKeep, repairedTitles } = partitionMdxPages( + pages, + changedFiles, + existingSummaries + ); + + const hasChanges = + deletedPageUrls.length > 0 || + pagesToSummarize.length > 0 || + repairedTitles > 0 || + toolkitPagesChanged(toolkitPages, mdxPageUrls, existingSummaries); + + console.log( + chalk.green( + `✓ ${pagesToKeep.length} pages unchanged, ${pagesToSummarize.length} pages to summarize${deletedPageUrls.length > 0 ? `, ${deletedPageUrls.length} pages deleted` : ""}` + ) + ); + + return { hasChanges, pagesToKeep, pagesToSummarize }; } /** @@ -779,6 +807,13 @@ async function summarizePagesInBatches( return summarizedPages; } +/** + * True when two llms.txt contents differ at most in their git-sha/date header + */ +function hasSameBody(a: string, b: string): boolean { + return a.replace(METADATA_REGEX, "") === b.replace(METADATA_REGEX, ""); +} + /** * Main execution function */ @@ -803,7 +838,7 @@ async function main() { if (previousMetadata) { console.log( chalk.gray( - ` Previous generation: ${previousMetadata.generationDate} (SHA: ${previousMetadata.gitSha.substring(0, SHA_SHORT_LENGTH)})` + ` Previous generation: ${previousMetadata.generationDate} (SHA: ${previousMetadata.gitSha.slice(0, SHA_SHORT_LENGTH)})` ) ); } @@ -887,8 +922,12 @@ async function main() { } } -// Run if called directly -if (import.meta.url === `file://${process.argv[1]}`) { +// Run if called directly (`bun scripts/generate-llmstxt.ts`), not when imported +// by tests. +if ( + process.argv[1] && + import.meta.url === pathToFileURL(process.argv[1]).href +) { main(); } diff --git a/scripts/lib/check-redirects-utils.ts b/scripts/lib/check-redirects-utils.ts index 48133192d..8fa39927d 100644 --- a/scripts/lib/check-redirects-utils.ts +++ b/scripts/lib/check-redirects-utils.ts @@ -5,6 +5,14 @@ * page still exists on disk, and matches redirects against moved files can * be unit tested without shelling out to git or touching the real * filesystem. + * + * Content layout (Fumadocs): + * content/docs/<path>.mdx -> /:locale/<path> + * content/docs/<path>/index.mdx -> /:locale/<path> + * content/docs/index.mdx -> /:locale + * + * Toolkit (MCP server) pages are not files: `lib/toolkit-route.tsx` renders + * `/:locale/resources/integrations/<category>/<toolkitId>` from JSON data. */ import { existsSync } from "node:fs"; @@ -18,14 +26,24 @@ export type Redirect = { permanent?: boolean; }; -const APP_LOCALE_PREFIX_REGEX = /^app\/[a-z]{2}\//; -const PAGE_FILE_SUFFIX_REGEX = /\/?page\.mdx?$/; +export const CONTENT_ROOT = "content/docs"; +export const TOOLKIT_ROUTE_MODULE = "lib/toolkit-route.tsx"; + +/** Matches a docs content file, e.g. `content/docs/guides/foo.mdx`. */ +export const CONTENT_FILE_REGEX = /^content\/docs\/.*\.mdx?$/; + +const CONTENT_PREFIX_REGEX = /^content\/docs\//; +const INDEX_FILE_REGEX = /(^|\/)index\.mdx?$/; +const MDX_EXTENSION_REGEX = /\.mdx?$/; const LOCALE_PREFIX_REGEX = /^\/:locale\/?/; -const PAGE_FILE_MATCH_REGEX = /page\.mdx?$/; const LOCALE_PATH_PREFIX_REGEX = /^\/:locale\//; const WILDCARD_PATH_REGEX = /\/:path\*.*$/; -const MDX_EXTENSION_REGEX = /\.mdx$/; const DYNAMIC_ROUTE_REGEX = /\[[^\]]+\]/; +const CATCH_ALL_SEGMENT_REGEX = /\[\.\.\.([^\]]+)\]/g; +const DYNAMIC_SEGMENT_REGEX = /\[([^\]]+)\]/g; + +const TOOLKIT_ROUTE_PREFIX = ["resources", "integrations"]; +const TOOLKIT_ROUTE_SEGMENT_COUNT = 4; export type DynamicRouteMove = { oldPath: string; @@ -34,52 +52,75 @@ export type DynamicRouteMove = { newUrl: string; }; +/** + * Strip the content root and the `.mdx`/`index.mdx` suffix from a file path, + * leaving the URL path without a leading slash (empty for the root page). + */ +function fileToRelativeUrlPath(filePath: string): string { + return filePath + .replace(CONTENT_PREFIX_REGEX, "") + .replace(INDEX_FILE_REGEX, "") + .replace(MDX_EXTENSION_REGEX, ""); +} + /** * Convert file path to URL path - * e.g., app/en/guides/foo/page.mdx -> /:locale/guides/foo + * e.g., content/docs/guides/foo.mdx -> /:locale/guides/foo + * content/docs/guides/foo/index.mdx -> /:locale/guides/foo */ export function fileToUrl(filePath: string): string { - const urlPath = filePath - .replace(APP_LOCALE_PREFIX_REGEX, "") - .replace(PAGE_FILE_SUFFIX_REGEX, ""); - + const urlPath = fileToRelativeUrlPath(filePath); return urlPath ? `/:locale/${urlPath}` : "/:locale"; } /** - * Convert URL path to file path - * e.g., /:locale/guides/foo -> app/en/guides/foo/page.mdx + * Convert URL path to the primary file path (the flat `<path>.mdx` form). + * e.g., /:locale/guides/foo -> content/docs/guides/foo.mdx + * + * A page may also live at `<path>/index.mdx`; use `urlToFileCandidates` when + * checking existence. */ export function urlToFile(urlPath: string): string { + return urlToFileCandidates(urlPath)[0]; +} + +/** + * All file paths that would serve a URL, in order of preference: + * `<path>.mdx`, `<path>/index.mdx`, then their `.md` variants. + * e.g., /:locale/guides/foo -> [content/docs/guides/foo.mdx, + * content/docs/guides/foo/index.mdx, ...] + */ +export function urlToFileCandidates(urlPath: string): string[] { const pathWithoutLocale = urlPath.replace(LOCALE_PREFIX_REGEX, ""); - return pathWithoutLocale - ? `app/en/${pathWithoutLocale}/page.mdx` - : "app/en/page.mdx"; + if (!pathWithoutLocale) { + return [`${CONTENT_ROOT}/index.mdx`, `${CONTENT_ROOT}/index.md`]; + } + const base = `${CONTENT_ROOT}/${pathWithoutLocale}`; + return [`${base}.mdx`, `${base}/index.mdx`, `${base}.md`, `${base}/index.md`]; } /** * Convert a file path containing a dynamic route to a URL pattern. * Replaces [param] with :param and [...param] with :param* - * e.g., app/en/resources/[toolkitId]/page.mdx -> /:locale/resources/:toolkitId + * e.g., content/docs/resources/[toolkitId].mdx -> /:locale/resources/:toolkitId */ export function dynamicFileToUrlPattern(filePath: string): string { - const urlPath = filePath - .replace(APP_LOCALE_PREFIX_REGEX, "") - .replace(PAGE_FILE_SUFFIX_REGEX, ""); + const urlPath = fileToRelativeUrlPath(filePath); // Replace [...param] with :param* (catch-all routes) // Replace [param] with :param (dynamic segments) const patternPath = urlPath - .replace(/\[\.\.\.([^\]]+)\]/g, ":$1*") - .replace(/\[([^\]]+)\]/g, ":$1"); + .replace(CATCH_ALL_SEGMENT_REGEX, ":$1*") + .replace(DYNAMIC_SEGMENT_REGEX, ":$1"); return patternPath ? `/:locale/${patternPath}` : "/:locale"; } /** - * Check if a dynamic route exists that could serve this URL path. - * e.g., for /resources/integrations/productivity/gmail, - * check if /resources/integrations/productivity/[toolkitId]/page.mdx exists + * Check if the toolkit route could serve this URL path. + * `lib/toolkit-route.tsx` renders every + * `/resources/integrations/<category>/<toolkitId>` URL from toolkit JSON, so + * such a URL stays reachable without an MDX file as long as that module exists. * * @param urlPath - The URL path to check * @param checkExists - Function to check if a file exists (defaults to fs.existsSync) @@ -91,25 +132,17 @@ export function dynamicRouteExists( const pathWithoutLocale = urlPath.replace(LOCALE_PREFIX_REGEX, ""); const segments = pathWithoutLocale.split("/").filter(Boolean); - // Try replacing segments with common dynamic route patterns - const dynamicPatterns = ["[toolkitId]", "[slug]", "[id]", "[...slug]"]; - - for (let i = segments.length - 1; i >= 0; i--) { - for (const pattern of dynamicPatterns) { - const testSegments = [...segments]; - testSegments[i] = pattern; - const testPath = `app/en/${testSegments.join("/")}/page.mdx`; - if (checkExists(testPath)) { - return true; - } - const testPathMd = testPath.replace(MDX_EXTENSION_REGEX, ".md"); - if (checkExists(testPathMd)) { - return true; - } - } + if (segments.length !== TOOLKIT_ROUTE_SEGMENT_COUNT) { + return false; + } + if ( + segments[0] !== TOOLKIT_ROUTE_PREFIX[0] || + segments[1] !== TOOLKIT_ROUTE_PREFIX[1] + ) { + return false; } - return false; + return checkExists(TOOLKIT_ROUTE_MODULE); } /** @@ -126,27 +159,19 @@ export function pageExists( return true; } - const filePath = urlToFile(urlPath); - if (checkExists(filePath)) { + if ( + urlToFileCandidates(urlPath).some((candidate) => checkExists(candidate)) + ) { return true; } - const mdPath = filePath.replace(MDX_EXTENSION_REGEX, ".md"); - if (checkExists(mdPath)) { - return true; - } - - // Check if a dynamic route could serve this URL - if (dynamicRouteExists(urlPath, checkExists)) { - return true; - } - - return false; + // Check if the toolkit route could serve this URL + return dynamicRouteExists(urlPath, checkExists); } /** - * Parse git diff output for renamed dynamic route page files. - * Detects when a page.mdx inside a dynamic route folder is moved. + * Parse git diff output for renamed dynamic route content files. + * Detects when a content file inside a dynamic route folder is moved. */ export function parseDynamicRouteMoves(output: string): DynamicRouteMove[] { const moves: DynamicRouteMove[] = []; @@ -155,17 +180,13 @@ export function parseDynamicRouteMoves(output: string): DynamicRouteMove[] { if (!line) { continue; } - const parts = line.split("\t"); - const status = parts[0]; + const [status, oldPath, newPath] = line.split("\t"); // Only look at renames (R followed by similarity percentage) if (!status?.startsWith("R")) { continue; } - const oldPath = parts[1]; - const newPath = parts[2]; - if (!(oldPath && newPath)) { continue; } @@ -175,7 +196,7 @@ export function parseDynamicRouteMoves(output: string): DynamicRouteMove[] { const newHasDynamic = DYNAMIC_ROUTE_REGEX.test(newPath); // We care about moves where the URL pattern changes - if (!PAGE_FILE_MATCH_REGEX.test(oldPath)) { + if (!CONTENT_FILE_REGEX.test(oldPath)) { continue; } diff --git a/scripts/lib/helm-chart-docs.ts b/scripts/lib/helm-chart-docs.ts index 9a612e8cd..8c3e2b545 100644 --- a/scripts/lib/helm-chart-docs.ts +++ b/scripts/lib/helm-chart-docs.ts @@ -108,15 +108,14 @@ export function escapeMdx(markdown: string): string { export function renderValuesPage(version: string, markdown: string): string { return `--- -title: "Helm values reference" -description: "Every configurable value in the Arcade Helm chart, generated from chart version ${version}" +title: Helm values reference +sidebarTitle: Values reference +description: Every configurable value in the Arcade Helm chart, generated from chart version ${version} --- -# Helm values reference - This reference is generated from Arcade Helm chart version \`${version}\`. -Set these values in a \`values.yaml\` file, or pass them with \`--set\`, when you install or upgrade the chart. For installation steps, see [Self-host Arcade with Helm](/operate/deploy/helm). +Set these values in a \`values.yaml\` file, or pass them with \`--set\`, when you install or upgrade the chart. For installation steps, see [Self-host Arcade with Helm](/en/operate/deploy/helm). ${escapeMdx(markdown)} `; @@ -127,17 +126,18 @@ export type SyncResult = { }; export function syncHelmChartDocs(options: { + archiveDir: string; chartDir: string; docsDir: string; version: string; }): SyncResult { - const { chartDir, docsDir, version } = options; + const { archiveDir, chartDir, docsDir, version } = options; if (!isValidVersion(version)) { throw new Error(`Invalid version: ${version}`); } const markdown = readValuesMarkdown(chartDir); - const versionDir = join(docsDir, "_chart", version); + const versionDir = join(archiveDir, version); mkdirSync(versionDir, { recursive: true }); writeFileSync(join(versionDir, "values.md"), `${markdown}\n`); @@ -154,10 +154,9 @@ export function syncHelmChartDocs(options: { } } - const pageDir = join(docsDir, "values-reference"); - mkdirSync(pageDir, { recursive: true }); + mkdirSync(docsDir, { recursive: true }); writeFileSync( - join(pageDir, "page.mdx"), + join(docsDir, "values-reference.mdx"), renderValuesPage(version, markdown) ); diff --git a/scripts/lib/update-links-utils.ts b/scripts/lib/update-links-utils.ts new file mode 100644 index 000000000..ada03baa8 --- /dev/null +++ b/scripts/lib/update-links-utils.ts @@ -0,0 +1,123 @@ +/** + * Pure helpers for scripts/update-internal-links.ts (kept separate so they can + * be unit-tested without running the script). + */ + +// Top-level regex patterns +const LOCALE_PREFIX_REGEX = /^\/:locale/; +const SPECIAL_REGEX_CHARS_REGEX = /[.*+?^${}()|[\]\\]/g; + +export interface Redirect { + destination: string; + source: string; +} + +export function isRootPath(path: string): boolean { + return path === "" || path === "/"; +} + +/** + * Filter redirects to only those that can be auto-updated + */ +export function getUpdatableRedirects(redirectList: Redirect[]): Redirect[] { + return redirectList.filter((r) => { + if (r.source.includes(":path*") || r.destination.includes(":path*")) { + return false; + } + + const sourceWithoutLocale = r.source.replace(LOCALE_PREFIX_REGEX, ""); + const destWithoutLocale = r.destination.replace(LOCALE_PREFIX_REGEX, ""); + + if (sourceWithoutLocale.includes(":") || destWithoutLocale.includes(":")) { + return false; + } + + // A bare locale root (e.g. `/:locale` -> `/:locale/home`) has no path to + // match; the patterns below would match every `](`, `href="` and backtick + // pair and inject the destination into unrelated text and code fences. + if (isRootPath(sourceWithoutLocale)) { + return false; + } + + if ( + r.destination.includes("REPLACE_WITH") || + r.destination.includes("TODO") + ) { + return false; + } + + return true; + }); +} + +/** + * Convert redirect to link patterns (without :locale prefix) + */ +export function redirectToLinkPatterns(redirect: Redirect): { + oldPath: string; + newPath: string; +} { + return { + newPath: redirect.destination.replace(LOCALE_PREFIX_REGEX, ""), + oldPath: redirect.source.replace(LOCALE_PREFIX_REGEX, ""), + }; +} + +/** + * Escape special regex characters + */ +export function escapeRegex(str: string): string { + return str.replace(SPECIAL_REGEX_CHARS_REGEX, "\\$&"); +} + +/** + * Update links in file content + */ +export function updateLinksInContent( + content: string, + oldPath: string, + newPath: string +): string { + let updated = content; + + // Markdown links: [text](/old-path) -> [text](/new-path) + // Using replacer function to avoid special replacement pattern interpretation + // (e.g., $1, $2, $& in newPath would be incorrectly expanded if using string) + const markdownLinkRegex = new RegExp( + `(\\]\\()${escapeRegex(oldPath)}([)"#\\s])`, + "g" + ); + updated = updated.replace( + markdownLinkRegex, + (_, p1, p2) => `${p1}${newPath}${p2}` + ); + + // JSX href with double quotes: href="/old-path" + const jsxDoubleQuoteRegex = new RegExp( + `(href=")${escapeRegex(oldPath)}(["#])`, + "g" + ); + updated = updated.replace( + jsxDoubleQuoteRegex, + (_, p1, p2) => `${p1}${newPath}${p2}` + ); + + // JSX href with single quotes: href='/old-path' + const jsxSingleQuoteRegex = new RegExp( + `(href=')${escapeRegex(oldPath)}(['#])`, + "g" + ); + updated = updated.replace( + jsxSingleQuoteRegex, + (_, p1, p2) => `${p1}${newPath}${p2}` + ); + + // Paths in backticks (code blocks showing links) + const backtickRegex = new RegExp(`(\`)${escapeRegex(oldPath)}(\`|[#])`, "g"); + updated = updated.replace( + backtickRegex, + (_, p1, p2) => `${p1}${newPath}${p2}` + ); + + return updated; +} diff --git a/scripts/sync-crawler-config.ts b/scripts/sync-crawler-config.ts index 62e550a29..0e36ec6c3 100644 --- a/scripts/sync-crawler-config.ts +++ b/scripts/sync-crawler-config.ts @@ -1,10 +1,10 @@ -#!/usr/bin/env npx tsx +#!/usr/bin/env bun /** * Push algolia/crawler-config.js to the Algolia Crawler, then trigger a reindex. * * Usage: - * pnpm sync-crawler-config + * bun run sync-crawler-config * * `PATCH /1/crawlers/{id}/config` is a top-level merge, so this only sends the * fields we manage. We deliberately do NOT send `apiKey`: the crawler's index @@ -80,7 +80,7 @@ console.log("Crawler config updated."); // (e.g. removeWordsIfNoResults) never reaches an existing production index // otherwise, and no-result rates stay high for compound agent queries. if (ADMIN_API_KEY) { - const settingsByIndex = crawlerConfig.initialIndexSettings ?? {}; + const settingsByIndex = crawlerConfig.initialIndexSettings; for (const [indexName, settings] of Object.entries(settingsByIndex)) { const response = await fetch( `https://${crawlerConfig.appId}-dsn.algolia.net/1/indexes/${encodeURIComponent(indexName)}/settings`, diff --git a/scripts/sync-helm-chart-docs.ts b/scripts/sync-helm-chart-docs.ts index 436636ecf..cc2ed3b03 100644 --- a/scripts/sync-helm-chart-docs.ts +++ b/scripts/sync-helm-chart-docs.ts @@ -1,19 +1,21 @@ -#!/usr/bin/env npx tsx +#!/usr/bin/env bun /** * Sync the generated Helm values reference for a released chart version. * * Usage: - * pnpm exec tsx scripts/sync-helm-chart-docs.ts --chart-dir <dir> --version <x.y.z> + * bun scripts/sync-helm-chart-docs.ts --chart-dir <dir> --version <x.y.z> * * <dir> is the unpacked chart (`helm pull ... --untar`). Writes the values - * reference page and raw copies under app/en/operate/deploy/helm. + * reference page under content/docs/operate/deploy/helm and raw copies under + * content/helm-chart, outside the docs source so they are not built as pages. */ import { parseArgs } from "node:util"; import { syncHelmChartDocs } from "./lib/helm-chart-docs"; -const DOCS_DIR = "app/en/operate/deploy/helm"; +const DOCS_DIR = "content/docs/operate/deploy/helm"; +const ARCHIVE_DIR = "content/helm-chart"; const { values } = parseArgs({ options: { @@ -31,6 +33,7 @@ if (!(chartDir && version)) { try { const { exampleCount } = syncHelmChartDocs({ + archiveDir: ARCHIVE_DIR, chartDir, docsDir: DOCS_DIR, version, diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json index e3f891ef2..b8a2d6e2b 100644 --- a/scripts/tsconfig.json +++ b/scripts/tsconfig.json @@ -1,10 +1,12 @@ { "extends": "../tsconfig.json", "compilerOptions": { - "baseUrl": "..", "paths": { - "@/*": ["./*"] - } + "@/*": ["../*"] + }, + "types": ["bun", "node"], + "plugins": [], + "incremental": false }, "include": [ "./**/*.ts" diff --git a/scripts/update-internal-links.ts b/scripts/update-internal-links.ts index e960a2702..845a31a9f 100644 --- a/scripts/update-internal-links.ts +++ b/scripts/update-internal-links.ts @@ -1,18 +1,23 @@ -#!/usr/bin/env npx tsx +#!/usr/bin/env bun /** * Update internal links to use redirect destinations instead of old paths * * Usage: - * pnpm update-links [--dry-run] + * bun run update-links [--dry-run] * * This script reads redirects from redirects.ts and updates any internal links - * in MDX/TSX files that point to redirected paths. + * in content (MDX) and component/route (TSX) files that point to redirected paths. */ import { readFileSync, writeFileSync } from "node:fs"; import fg from "fast-glob"; import { redirects as configuredRedirects } from "../redirects"; +import { + getUpdatableRedirects, + redirectToLinkPatterns, + updateLinksInContent, +} from "./lib/update-links-utils"; // Colors for terminal output const colors = { @@ -25,114 +30,6 @@ const colors = { // Parse command line arguments const dryRun = process.argv.includes("--dry-run"); -// Top-level regex patterns -const LOCALE_PREFIX_REGEX = /^\/:locale/; -const SPECIAL_REGEX_CHARS_REGEX = /[.*+?^${}()|[\]\\]/g; - -type Redirect = { - source: string; - destination: string; -}; - -/** - * Filter redirects to only those that can be auto-updated - */ -function getUpdatableRedirects(redirectList: Redirect[]): Redirect[] { - return redirectList.filter((r) => { - if (r.source.includes(":path*") || r.destination.includes(":path*")) { - return false; - } - - const sourceWithoutLocale = r.source.replace(LOCALE_PREFIX_REGEX, ""); - const destWithoutLocale = r.destination.replace(LOCALE_PREFIX_REGEX, ""); - - if (sourceWithoutLocale.includes(":") || destWithoutLocale.includes(":")) { - return false; - } - - if ( - r.destination.includes("REPLACE_WITH") || - r.destination.includes("TODO") - ) { - return false; - } - - return true; - }); -} - -/** - * Convert redirect to link patterns (without :locale prefix) - */ -function redirectToLinkPatterns(redirect: Redirect): { - oldPath: string; - newPath: string; -} { - return { - oldPath: redirect.source.replace(LOCALE_PREFIX_REGEX, ""), - newPath: redirect.destination.replace(LOCALE_PREFIX_REGEX, ""), - }; -} - -/** - * Escape special regex characters - */ -function escapeRegex(str: string): string { - return str.replace(SPECIAL_REGEX_CHARS_REGEX, "\\$&"); -} - -/** - * Update links in file content - */ -function updateLinksInContent( - content: string, - oldPath: string, - newPath: string -): string { - let updated = content; - - // Markdown links: [text](/old-path) -> [text](/new-path) - // Using replacer function to avoid special replacement pattern interpretation - // (e.g., $1, $2, $& in newPath would be incorrectly expanded if using string) - const markdownLinkRegex = new RegExp( - `(\\]\\()${escapeRegex(oldPath)}([)"#\\s])`, - "g" - ); - updated = updated.replace( - markdownLinkRegex, - (_, p1, p2) => `${p1}${newPath}${p2}` - ); - - // JSX href with double quotes: href="/old-path" - const jsxDoubleQuoteRegex = new RegExp( - `(href=")${escapeRegex(oldPath)}(["#])`, - "g" - ); - updated = updated.replace( - jsxDoubleQuoteRegex, - (_, p1, p2) => `${p1}${newPath}${p2}` - ); - - // JSX href with single quotes: href='/old-path' - const jsxSingleQuoteRegex = new RegExp( - `(href=')${escapeRegex(oldPath)}(['#])`, - "g" - ); - updated = updated.replace( - jsxSingleQuoteRegex, - (_, p1, p2) => `${p1}${newPath}${p2}` - ); - - // Paths in backticks (code blocks showing links) - const backtickRegex = new RegExp(`(\`)${escapeRegex(oldPath)}(\`|[#])`, "g"); - updated = updated.replace( - backtickRegex, - (_, p1, p2) => `${p1}${newPath}${p2}` - ); - - return updated; -} - // ============================================================ // Main script // ============================================================ @@ -161,8 +58,19 @@ if (redirects.length === 0) { console.log(colors.blue("Scanning files for internal links to update...")); console.log(""); -// Find all MDX, MD, and TSX files in app directory -const files = fg.sync(["app/**/*.mdx", "app/**/*.md", "app/**/*.tsx"], { +// Content pages plus the TS/TSX modules that render hardcoded internal links. +// Keep in sync with the `update-links` globs in .husky/pre-commit. +export const LINK_FILE_GLOBS = [ + "content/docs/**/*.mdx", + "content/docs/**/*.md", + "components/**/*.tsx", + "app/**/*.tsx", + "lib/**/*.ts", + "lib/**/*.tsx", + "data/**/*.ts", +]; + +const files = fg.sync(LINK_FILE_GLOBS, { ignore: ["**/node_modules/**"], }); diff --git a/scripts/vale-editorial.ts b/scripts/vale-editorial.ts index db9346e7d..6fac25359 100644 --- a/scripts/vale-editorial.ts +++ b/scripts/vale-editorial.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env npx tsx +#!/usr/bin/env bun /** * Editorial Review for Merged Pull Requests @@ -7,8 +7,8 @@ * and create a follow-up PR with structural improvements. * * Usage: - * pnpm vale:editorial --pr 123 - * GITHUB_TOKEN=xxx ANTHROPIC_API_KEY=xxx pnpm vale:editorial --pr 123 + * bun run vale:editorial --pr 123 + * GITHUB_TOKEN=xxx ANTHROPIC_API_KEY=xxx bun run vale:editorial --pr 123 * * Prefers Anthropic (Claude) if ANTHROPIC_API_KEY is set, falls back to OpenAI. */ @@ -215,7 +215,7 @@ async function getPRInfo(octokit: Octokit, prNumber: number): Promise<PRInfo> { }); const docFiles = files - .filter((f) => f.filename.startsWith("app/en/")) + .filter((f) => f.filename.startsWith("content/docs/")) .filter((f) => f.filename.endsWith(".md") || f.filename.endsWith(".mdx")) .filter((f) => f.status !== "removed") .slice(0, MAX_FILES) diff --git a/scripts/vale-fix.ts b/scripts/vale-fix.ts index dc61b8041..f1a072efb 100644 --- a/scripts/vale-fix.ts +++ b/scripts/vale-fix.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env npx tsx +#!/usr/bin/env bun /** * Vale + AI Fix Script (Interactive) @@ -7,9 +7,9 @@ * with AI assistance. Supports feedback loops for iterative refinement. * * Usage: - * pnpm vale:fix [files...] # Fix specific files - * pnpm vale:fix --staged # Fix staged files only - * pnpm vale:fix app/en/guides/ # Fix all files in directory + * bun run vale:fix [files...] # Fix specific files + * bun run vale:fix --staged # Fix staged files only + * bun run vale:fix content/docs/guides/ # Fix all files in directory */ import { execSync, spawnSync } from "node:child_process"; @@ -426,7 +426,7 @@ function getFilesToCheck(args: string[]): string[] { .filter(Boolean); } if (args.length === 0) { - console.log("Usage: pnpm vale:fix [--staged | files...]"); + console.log("Usage: bun run vale:fix [--staged | files...]"); process.exit(0); } return args; @@ -556,8 +556,8 @@ async function processIssue( showIssueDiff(currentContent, fixed, issue); - const iterating = true; - while (iterating) { + // Loops until the user picks an action that resolves the issue. + for (;;) { const answer = await prompt( `\n ${colors.green}[y]es${colors.reset} / ${colors.red}[n]o${colors.reset} / ${colors.blue}[e]dit${colors.reset} / ${colors.yellow}[f]eedback${colors.reset} / ${colors.dim}[s]kip file${colors.reset}: ` ); @@ -579,8 +579,6 @@ async function processIssue( return { content: currentContent, action: "skipped" }; } } - - return { content: currentContent, action: "skipped" }; } /** diff --git a/scripts/vale-style-review.ts b/scripts/vale-style-review.ts index 3c7381907..941d78782 100644 --- a/scripts/vale-style-review.ts +++ b/scripts/vale-style-review.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env npx tsx +#!/usr/bin/env bun /** * Vale Style Review for Pull Requests @@ -7,9 +7,9 @@ * and posts suggestions as GitHub PR review comments. * * Usage: - * pnpm vale:review --pr 123 - * GITHUB_TOKEN=xxx ANTHROPIC_API_KEY=xxx pnpm vale:review --pr 123 - * GITHUB_TOKEN=xxx OPENAI_API_KEY=xxx pnpm vale:review --pr 123 + * bun run vale:review --pr 123 + * GITHUB_TOKEN=xxx ANTHROPIC_API_KEY=xxx bun run vale:review --pr 123 + * GITHUB_TOKEN=xxx OPENAI_API_KEY=xxx bun run vale:review --pr 123 * * Prefers Anthropic (Claude) if ANTHROPIC_API_KEY is set, falls back to OpenAI. */ @@ -161,7 +161,7 @@ async function getChangedFiles( }); return files - .filter((f) => f.filename.startsWith("app/en/")) + .filter((f) => f.filename.startsWith("content/docs/")) .filter((f) => f.filename.endsWith(".md") || f.filename.endsWith(".mdx")) .filter((f) => f.status !== "removed") .slice(0, MAX_FILES) @@ -354,11 +354,8 @@ async function getSuggestionsFromAnthropic( } // Extract JSON from response (may be wrapped in markdown code blocks) - let jsonText = textBlock.text; - const jsonMatch = jsonText.match(JSON_CODE_BLOCK_REGEX); - if (jsonMatch) { - jsonText = jsonMatch[1]; - } + const [, fencedJson] = textBlock.text.match(JSON_CODE_BLOCK_REGEX) ?? []; + const jsonText = fencedJson ?? textBlock.text; const parsed = JSON.parse(jsonText); return Array.isArray(parsed.suggestions) ? parsed.suggestions : []; @@ -430,7 +427,7 @@ function countLeadingFenceChars(trimmedLine: string): { count: number; char: string; } { - const firstChar = trimmedLine[0]; + const [firstChar] = trimmedLine; if (firstChar !== "`" && firstChar !== "~") { return { count: 0, char: "" }; } diff --git a/source.config.ts b/source.config.ts new file mode 100644 index 000000000..6301ebed2 --- /dev/null +++ b/source.config.ts @@ -0,0 +1,230 @@ +import fs from "node:fs"; +import path from "node:path"; +import { + type RehypeCodeOptions, + rehypeCodeDefaultOptions, + remarkMdxMermaid, +} from "fumadocs-core/mdx-plugins"; +import { + defineConfig, + defineDocs, + frontmatterSchema, +} from "fumadocs-mdx/config"; +import { z } from "zod"; +import { remarkGlossary } from "./lib/remark-glossary"; +import { remarkNextraCompat } from "./lib/remark-nextra-compat"; +import { remarkSmartypants } from "./lib/remark-smartypants"; + +/** + * Frontmatter accepted on every docs page. `metaTitle` overrides the `<title>` + * when it differs from the rendered H1; `sidebarTitle` is the sidebar label; + * `toc`/`breadcrumb` hide the table of contents or breadcrumb; `icon` is a + * Lucide icon name (resolved by `lucideIconsPlugin` in lib/source.ts). + */ +const docsFrontmatterSchema = frontmatterSchema.extend({ + breadcrumb: z.boolean().optional(), + icon: z.string().optional(), + // Landing-style page (Nextra layout: full + toc/breadcrumb off): no title, + // description or page actions are rendered around the MDX body. + landing: z.boolean().optional(), + // Freshness: the date a human last ran the page end-to-end, and how many + // days it stays "verified" (components/verified-badge.tsx). + lastVerified: z.string().optional(), + metaTitle: z.string().optional(), + sidebarTitle: z.string().optional(), + toc: z.boolean().optional(), + verifyEvery: z.number().int().positive().optional(), +}); + +export const docs = defineDocs({ + dir: "content/docs", + docs: { + // Git-based "last updated" date shown under each page. + lastModified: true, + // Exposes `page.data.getText("processed")`, used by app/llms.mdx for the + // "copy page as Markdown" action and agent-friendly page exports. + postprocess: { includeProcessedMarkdown: true }, + schema: docsFrontmatterSchema, + }, +}); + +/** Finds the glossary page, which may be a file or a folder index. */ +function resolveGlossaryPath(): string { + const candidates = [ + "content/docs/resources/glossary.mdx", + "content/docs/resources/glossary/index.mdx", + ]; + return ( + candidates.find((candidate) => + fs.existsSync(path.join(process.cwd(), candidate)) + ) ?? candidates[0] + ); +} + +type ShikiTransformer = NonNullable<RehypeCodeOptions["transformers"]>[number]; + +const LANGUAGE_CLASS_PREFIX = "language-"; +const COPY_DISABLED_REGEX = /( |^)copy=false($| )/; +const HIGHLIGHT_GROUP_REGEX = /\B\{(.*?)\}\B/g; +const SINGLE_NUMBER_REGEX = /^-?\d+$/; +const NUMBER_RANGE_REGEX = /^(-?\d+)(-|\.\.\.?|\u2025|\u2026|\u22EF)(-?\d+)$/; +const INCLUSIVE_SEPARATORS = new Set(["-", "..", "\u2025"]); + +/** Languages Nextra rendered without token colours. */ +const PLAIN_LANGUAGES = new Set(["plaintext", "text", "txt", "plain"]); + +type FenceMeta = { + __raw?: string; + _fenceLang?: string; + "data-line-numbers"?: boolean; + title?: string; +}; + +function classList(value: unknown): string[] { + if (Array.isArray(value)) { + return value.map(String); + } + return typeof value === "string" ? value.split(" ") : []; +} + +/** `parse-numeric-range`, as used by rehype-pretty-code: "1-3, 7" → 1,2,3,7. */ +function parseNumericRange(input: string): number[] { + const result: number[] = []; + for (const part of input.split(",").map((value) => value.trim())) { + if (SINGLE_NUMBER_REGEX.test(part)) { + result.push(Number.parseInt(part, 10)); + continue; + } + const match = part.match(NUMBER_RANGE_REGEX); + if (!match) { + continue; + } + const start = Number.parseInt(match[1] ?? "", 10); + const step = start < Number.parseInt(match[3] ?? "", 10) ? 1 : -1; + let end = Number.parseInt(match[3] ?? "", 10); + if (INCLUSIVE_SEPARATORS.has(match[2] ?? "")) { + end += step; + } + for (let line = start; line !== end; line += step) { + result.push(line); + } + } + return result; +} + +/** + * Lines to highlight: every `{…}` group of the fence meta, the way Nextra's + * rehype-pretty-code read them (`{1-5} {16}`, `{9-18, 20, 24}` with spaces). + */ +function highlightedLines(meta: string): Set<number> { + const lines = new Set<number>(); + for (const match of meta.matchAll(HIGHLIGHT_GROUP_REGEX)) { + for (const line of parseNumericRange(match[1] ?? "")) { + lines.add(line); + } + } + return lines; +} + +/** The language written on the fence, before any fallback to plain text. */ +function fenceLanguage(pre: unknown): string | undefined { + const code = ( + pre as { children?: { properties?: { className?: unknown } }[] } + ).children?.[0]; + const languageClass = classList(code?.properties?.className).find((name) => + name.startsWith(LANGUAGE_CLASS_PREFIX) + ); + return languageClass?.slice(LANGUAGE_CLASS_PREFIX.length); +} + +/** + * Shapes Shiki's output like Nextra's (rehype-pretty-code) so the ported + * `pre`/`code` components and CSS apply unchanged: + * `<pre data-language data-copy data-word-wrap [data-filename]>` holding + * `<code [data-line-numbers]>` with one bare `<span>` per line, highlighted + * lines marked `data-highlighted-line`. Unknown and plain-text languages keep + * the fence language as the label and carry no token colours. + */ +const transformerNextraShape: ShikiTransformer = { + code(node) { + const meta = this.options.meta as FenceMeta | undefined; + if (meta?.["data-line-numbers"]) { + const lines = node.children.filter((child) => child.type === "element"); + node.properties["data-line-numbers"] = ""; + node.properties["data-line-numbers-max-digits"] = String( + String(lines.length).length + ); + } + }, + enforce: "post", + line(node, line) { + const meta = this.options.meta as FenceMeta | undefined; + if (highlightedLines(meta?.__raw ?? "").has(line)) { + node.properties["data-highlighted-line"] = ""; + } + node.properties.class = undefined; + if (node.children.length === 0) { + node.children.push({ type: "text", value: " " }); + } + }, + name: "arcade:nextra-shape", + pre(node) { + const meta = this.options.meta as FenceMeta | undefined; + const raw = meta?.__raw ?? ""; + const language = meta?._fenceLang ?? this.options.lang; + node.properties = { + "data-copy": COPY_DISABLED_REGEX.test(raw) ? undefined : "", + "data-filename": meta?.title, + "data-language": language || "plaintext", + "data-word-wrap": raw.includes("word-wrap=false") ? undefined : "", + tabindex: "0", + }; + }, + span(node) { + const meta = this.options.meta as FenceMeta | undefined; + const language = meta?._fenceLang ?? this.options.lang; + const isFallback = + Boolean(meta?._fenceLang) && meta?._fenceLang !== this.options.lang; + if (isFallback || PLAIN_LANGUAGES.has(language)) { + node.properties.style = undefined; + } + }, +}; + +const glossaryPath = resolveGlossaryPath(); +const hasGlossary = fs.existsSync(path.join(process.cwd(), glossaryPath)); + +export default defineConfig({ + mdxOptions: { + rehypeCodeOptions: { + ...rehypeCodeDefaultOptions, + // Unknown fence languages (for example `env`) render as plain text, as + // they did in Nextra, instead of failing the build. + fallbackLanguage: "plaintext", + // Nextra showed no language icon; the header comes from components/mdx/pre.tsx. + icon: false, + parseMetaString(meta, node, tree) { + return { + ...rehypeCodeDefaultOptions.parseMetaString?.(meta, node, tree), + _fenceLang: fenceLanguage(node), + }; + }, + tab: false, + // Only the Nextra-shaped output; Fumadocs' `// [!code …]` comment + // notations are left out because production prints such comments as-is. + transformers: [transformerNextraShape], + }, + remarkPlugins: (plugins) => { + // remarkMdxMermaid turns ```mermaid fences into <Mermaid chart /> (see + // components/mdx/mermaid.tsx), matching Nextra, which rendered diagrams. + const extra: typeof plugins = [remarkMdxMermaid]; + if (hasGlossary) { + extra.push([remarkGlossary, { glossaryPath }]); + } + extra.push(remarkSmartypants); + // remarkNextraCompat assigns the heading ids, so it must come before + // Fumadocs' own remarkHeading (in `plugins`), which then keeps them. + return [remarkNextraCompat, ...plugins, ...extra]; + }, + }, +}); diff --git a/tests/auth-provider-pages.test.ts b/tests/auth-provider-pages.test.ts index 4e71b8663..2fe841418 100644 --- a/tests/auth-provider-pages.test.ts +++ b/tests/auth-provider-pages.test.ts @@ -1,29 +1,22 @@ -import { readdirSync } from "node:fs"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; -import { getAuthProviderDocsUrl } from "@/app/_components/toolkit-docs/constants"; +import { getAuthProviderDocsUrl } from "@/components/toolkit-docs/constants"; import { redirects } from "@/redirects"; import { AUTH_PROVIDER_PAGE_IDS, REDIRECTED_AUTH_PROVIDER_IDS, } from "@/toolkit-docs-generator/src/shared/auth-provider-pages"; +import { CONTENT_ROOT, listChildSlugs } from "./helpers/content-paths"; -const AUTH_PROVIDERS_DIR = join( - process.cwd(), - "app", - "en", - "references", - "auth-providers" -); +const AUTH_PROVIDERS_DIR = join(CONTENT_ROOT, "references", "auth-providers"); describe("AUTH_PROVIDER_PAGE_IDS", () => { - it("matches the page directories under app/en/references/auth-providers", () => { - const pageDirs = readdirSync(AUTH_PROVIDERS_DIR, { withFileTypes: true }) - .filter((entry) => entry.isDirectory()) - .map((entry) => entry.name) - .sort(); + it("matches the pages under content/docs/references/auth-providers", () => { + // Each provider page is `<id>.mdx` (or `<id>/index.mdx`); the folder's own + // `index.mdx` is the section overview, not a provider. + const pageSlugs = listChildSlugs(AUTH_PROVIDERS_DIR); - expect([...AUTH_PROVIDER_PAGE_IDS].sort()).toEqual(pageDirs); + expect([...AUTH_PROVIDER_PAGE_IDS].sort()).toEqual(pageSlugs); }); }); diff --git a/tests/available-tools-filter-behavior.test.ts b/tests/available-tools-filter-behavior.test.ts index 87fc97e13..ce8ada2e4 100644 --- a/tests/available-tools-filter-behavior.test.ts +++ b/tests/available-tools-filter-behavior.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { filterTools } from "@/app/_components/toolkit-docs/components/available-tools-filter"; +import { filterTools } from "@/components/toolkit-docs/components/available-tools-filter"; const makeTool = ( name: string, diff --git a/tests/available-tools-filter-operations.test.ts b/tests/available-tools-filter-operations.test.ts index 56c9796aa..ee78feecb 100644 --- a/tests/available-tools-filter-operations.test.ts +++ b/tests/available-tools-filter-operations.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { filterTools } from "@/app/_components/toolkit-docs/components/available-tools-filter"; +import { filterTools } from "@/components/toolkit-docs/components/available-tools-filter"; const makeTool = (name: string, operations: string[]) => ({ name, diff --git a/tests/broken-link-check.test.ts b/tests/broken-link-check.test.ts index 593a89eac..3822011c4 100644 --- a/tests/broken-link-check.test.ts +++ b/tests/broken-link-check.test.ts @@ -1,12 +1,17 @@ import { existsSync, readdirSync, readFileSync } from "node:fs"; import { join } from "node:path"; -import fg from "fast-glob"; import { scanURLs, validateFiles } from "next-validate-link"; import { expect, test } from "vitest"; import { INTEGRATION_CATEGORIES, normalizeToolkitId, } from "@/toolkit-docs-generator/src/shared/toolkit-primitives"; +import { + contentFileToUrl, + listContentFiles, + resolveContentFile, + toContentRelativePath, +} from "./helpers/content-paths"; const TIMEOUT = 30_000; @@ -182,7 +187,22 @@ function validateToolkitIntegrationRoute( test( "check for broken links", async () => { + // Every MDX file under content/docs is served by the optional catch-all + // route app/en/[[...slug]]/page.tsx. next-validate-link only sees that + // route file, so populate the `slug` param with each content page's URL + // segments (content/docs/a/b.mdx and content/docs/a/b/index.mdx both map + // to /en/a/b; content/docs/index.mdx maps to /en). + const contentFiles = await listContentFiles(); + const slugValues = contentFiles.map((file) => ({ + value: toContentRelativePath(contentFileToUrl(file)) + .split("/") + .filter(Boolean), + })); + const scanned = await scanURLs({ + populate: { + "en/[[...slug]]": slugValues, + }, preset: "next", }); @@ -230,7 +250,7 @@ test( } // Check if the base path exists with locale prefix - const baseExists = scanned.urls?.has(localeUrl); + const baseExists = scanned.urls.has(localeUrl); if (!baseExists) { return false; @@ -238,15 +258,10 @@ test( // If there's a fragment, validate it by checking the actual file content if (fragment) { - // Convert URL path to file path - const filePath = join( - process.cwd(), - "app", - "en", - path.slice(1), - "page.mdx" + const filePath = resolveContentFile(path); + return ( + filePath !== null && validateAnchorFragment(filePath, fragment) ); - return validateAnchorFragment(filePath, fragment); } // If no fragment, the base path exists so it's valid @@ -256,7 +271,15 @@ test( return false; }; - const found = await validateFiles(await fg("app/**/*.{md,mdx}"), { + // Pass each page's own URL so relative links (`./sibling`) resolve + // against the page, not against the file system path. + const files = contentFiles.map((file) => ({ + content: readFileSync(file, "utf-8"), + path: file, + url: contentFileToUrl(file), + })); + + const found = await validateFiles(files, { scanned, whitelist, }); diff --git a/tests/chrome-no-mailto.test.ts b/tests/chrome-no-mailto.test.ts index 736bffeb8..5c6eb1c0f 100644 --- a/tests/chrome-no-mailto.test.ts +++ b/tests/chrome-no-mailto.test.ts @@ -12,24 +12,36 @@ const LINE_COMMENT = /(^|[^:])\/\/.*$/; const NON_NEWLINE = /[^\n]/g; /** - * Sitewide chrome (footer, navbar, etc. in app/_components) must not contain a + * Sitewide chrome (footer, navbar, etc. in components/) must not contain a * raw email link: Cloudflare Email Obfuscation rewrites it into a * /cdn-cgi/l/email-protection URL that returns 404 to crawlers, so one in * shared chrome becomes a broken link on every page. Link to * /en/resources/contact-us instead. In-content email links on individual pages - * are fine and are not scanned here. + * are fine and are not scanned here, and neither are page-local components + * under components/pages/ (they render on one page only). * - * Exception: app/_components/contact-email.tsx is the sanctioned helper that + * Exception: components/contact-email.tsx is the sanctioned helper that * assembles a mailto only on the client (after mount) and renders a plain * contact-page link in SSR, so it never emits a raw email link for crawlers. */ test( "shared components contain no mailto: or email-protection links", async () => { - const files = await fg("app/_components/**/*.{ts,tsx}", { - // The sanctioned client-only mailto helper (see docstring) is exempt. - ignore: ["app/_components/contact-email.tsx"], - }); + const files = await fg( + [ + "components/**/*.{ts,tsx}", + "app/**/*.{ts,tsx}", + "lib/layout.shared.tsx", + ], + { + ignore: [ + // The sanctioned client-only mailto helper (see docstring) is exempt. + "components/contact-email.tsx", + // Page-local components are not sitewide chrome. + "components/pages/**", + ], + } + ); const offenders: Array<{ file: string; line: number; match: string }> = []; for (const file of files) { diff --git a/tests/content-frontmatter.test.ts b/tests/content-frontmatter.test.ts new file mode 100644 index 000000000..bf3d53473 --- /dev/null +++ b/tests/content-frontmatter.test.ts @@ -0,0 +1,79 @@ +import { readFileSync } from "node:fs"; +import { describe, expect, test } from "vitest"; +import { + listContentFiles, + readFrontmatterValue, + stripFrontmatter, +} from "./helpers/content-paths"; + +/** + * Fumadocs renders the frontmatter `title` as the page heading (<DocsTitle>) + * and uses it for the sidebar label and <title>. A page without one renders an + * empty heading and an unnamed sidebar entry, and a body that still opens with + * a `# Title` H1 renders the heading twice. + */ + +const IMPORT_LINE_REGEX = /^import\s[^\n]*$/; +const H1_REGEX = /^#\s+(.+?)\s*#*\s*$/; +const INLINE_CODE_REGEX = /`/g; +const WHITESPACE_REGEX = /\s+/g; + +const normalizeHeading = (value: string): string => + value + .replace(INLINE_CODE_REGEX, "") + .replace(WHITESPACE_REGEX, " ") + .trim() + .toLowerCase(); + +/** First non-empty, non-import line of the MDX body. */ +const firstBodyLine = (content: string): string | null => { + for (const line of stripFrontmatter(content).split("\n")) { + const trimmed = line.trim(); + if (!trimmed || IMPORT_LINE_REGEX.test(trimmed)) { + continue; + } + return trimmed; + } + return null; +}; + +describe("content/docs frontmatter", () => { + test("every page declares a non-empty title", async () => { + const files = await listContentFiles(); + expect(files.length).toBeGreaterThan(0); + + const missing = files.filter((file) => { + const title = readFrontmatterValue(readFileSync(file, "utf-8"), "title"); + return !title; + }); + + for (const file of missing) { + console.error(`${file}: missing frontmatter "title"`); + } + expect(missing).toEqual([]); + }); + + test("no page body opens with an H1 that duplicates its title", async () => { + const files = await listContentFiles(); + + const duplicates = files.filter((file) => { + const content = readFileSync(file, "utf-8"); + const title = readFrontmatterValue(content, "title"); + const first = firstBodyLine(content); + if (!(title && first)) { + return false; + } + const h1 = H1_REGEX.exec(first)?.[1]; + return ( + h1 !== undefined && normalizeHeading(h1) === normalizeHeading(title) + ); + }); + + for (const file of duplicates) { + console.error( + `${file}: body starts with an H1 equal to the frontmatter title; Fumadocs already renders the title, so remove the H1` + ); + } + expect(duplicates).toEqual([]); + }); +}); diff --git a/tests/copilotkit-guide.test.ts b/tests/copilotkit-guide.test.ts index dd58f3b30..7c547efa6 100644 --- a/tests/copilotkit-guide.test.ts +++ b/tests/copilotkit-guide.test.ts @@ -5,51 +5,52 @@ import { existsSync, readFileSync } from "node:fs"; import { join } from "node:path"; import { describe, expect, test } from "vitest"; +import { + CONTENT_ROOT, + readFrontmatterValue, + readMetaPages, + resolveContentFile, +} from "./helpers/content-paths"; -// Key and title asserted together, not as two unrelated substrings. -const META_KEY_RE = /copilotkit:\s*\{\s*title:\s*"CopilotKit"/; -// Frontmatter keys must carry real values, not just be present. -const FRONTMATTER_TITLE_RE = /title:\s*"[^"]+"/; -const FRONTMATTER_DESCRIPTION_RE = /description:\s*"[^"]+"/; // The install step must pull in zod — the code samples import it, and it // otherwise resolves only as a transitive/peer dep. Matches `npm install … zod` // or `pnpm add … zod`. const INSTALL_ZOD_RE = /(?:install|add)\b[^\n]*\bzod\b/; -const GUIDE_PAGE = "app/en/get-started/agent-frameworks/copilotkit/page.mdx"; +const FRAMEWORKS_DIR = join(CONTENT_ROOT, "get-started", "agent-frameworks"); +const GUIDE_URL = "/en/get-started/agent-frameworks/copilotkit"; function read(p: string): string { return existsSync(p) ? readFileSync(p, "utf-8") : ""; } describe("CopilotKit agent-framework guide", () => { - test("_meta.tsx registers a copilotkit key titled CopilotKit", () => { - const content = read( - join(process.cwd(), "app/en/get-started/agent-frameworks/_meta.tsx") - ); - expect(content).toMatch(META_KEY_RE); + test("meta.json lists the copilotkit page", () => { + expect(readMetaPages(FRAMEWORKS_DIR)).toContain("copilotkit"); }); test("agent-framework-tabs.tsx has a CopilotKit PlatformCard linking to the guide", () => { const content = read( - join(process.cwd(), "app/_components/agent-framework-tabs.tsx") + join(process.cwd(), "components/agent-framework-tabs.tsx") ); expect(content).toContain("PlatformCard"); expect(content).toContain('name="CopilotKit"'); - expect(content).toContain( - 'link="/en/get-started/agent-frameworks/copilotkit"' - ); + expect(content).toContain(`link="${GUIDE_URL}"`); }); - test("the copilotkit page exists with a non-empty title and description", () => { - const filePath = join( - process.cwd(), - "app/en/get-started/agent-frameworks/copilotkit/page.mdx" - ); - expect(existsSync(filePath)).toBe(true); - const content = read(filePath); - expect(content).toMatch(FRONTMATTER_TITLE_RE); - expect(content).toMatch(FRONTMATTER_DESCRIPTION_RE); + test("the copilotkit page exists, is labelled CopilotKit in the sidebar, and has a description", () => { + const filePath = resolveContentFile(GUIDE_URL); + expect(filePath, `Expected a content page for ${GUIDE_URL}`).not.toBeNull(); + const content = read(filePath ?? ""); + // Frontmatter keys must carry real values, not just be present. + // Fumadocs reads the sidebar label from `sidebarTitle` (falling back to + // `title`). + expect(readFrontmatterValue(content, "title")).toBeTruthy(); + const sidebarLabel = + readFrontmatterValue(content, "sidebarTitle") ?? + readFrontmatterValue(content, "title"); + expect(sidebarLabel).toBe("CopilotKit"); + expect(readFrontmatterValue(content, "description")).toBeTruthy(); }); test("the CopilotKit icon exists and is a well-formed SVG", () => { @@ -65,7 +66,7 @@ describe("CopilotKit agent-framework guide", () => { // scaffold path must be one that actually exists, the code samples must be the // versions verified to typecheck/lint, and the prose-only steps must ship code. describe("CopilotKit guide content reflects the validated fixes", () => { - const content = read(join(process.cwd(), GUIDE_PAGE)); + const content = read(resolveContentFile(GUIDE_URL) ?? ""); test("scaffolds with create-next-app, not the interactive copilotkit CLI", () => { expect(content).toContain("create-next-app"); @@ -82,7 +83,8 @@ describe("CopilotKit guide content reflects the validated fixes", () => { }); test("shows the client-side CopilotKit provider wiring", () => { - expect(content).toContain('filename="app/providers.tsx"'); + // Fumadocs code-fence meta names the file with `title=`. + expect(content).toContain('title="app/providers.tsx"'); expect(content).toContain("useSingleEndpoint"); }); }); diff --git a/tests/filter-params.test.ts b/tests/filter-params.test.ts index 3169bfe87..16bc14dac 100644 --- a/tests/filter-params.test.ts +++ b/tests/filter-params.test.ts @@ -8,7 +8,7 @@ import { isToolkitType, parseFiltersFromParams, serializeFiltersToParams, -} from "../app/en/resources/integrations/components/filter-params"; +} from "../components/pages/integrations/filter-params"; describe("isToolkitType", () => { test("accepts known toolkit types", () => { diff --git a/tests/helpers/content-paths.ts b/tests/helpers/content-paths.ts new file mode 100644 index 000000000..6524dc515 --- /dev/null +++ b/tests/helpers/content-paths.ts @@ -0,0 +1,180 @@ +/** + * Shared helpers for tests that walk the Fumadocs content tree. + * + * Layout: + * content/docs/<path>.mdx -> /en/<path> + * content/docs/<path>/index.mdx -> /en/<path> + * content/docs/index.mdx -> /en + * content/docs/**\/meta.json -> sidebar order (`pages` array) + */ + +import { existsSync, readdirSync, readFileSync } from "node:fs"; +import { join, relative, sep } from "node:path"; +import fg from "fast-glob"; + +export const CONTENT_ROOT_RELATIVE = "content/docs"; +export const CONTENT_ROOT = join(process.cwd(), "content", "docs"); +export const LOCALE_PREFIX = "/en"; + +const MDX_EXTENSION_REGEX = /\.mdx?$/; +const INDEX_FILE_REGEX = /(^|\/)index\.mdx?$/; +const LEADING_SLASH_REGEX = /^\/+/; +const TRAILING_SLASH_REGEX = /\/+$/; +const LOCALE_PREFIX_REGEX = /^\/(en|es|pt-BR)(?=\/|$)/; +const FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---/; +const QUOTED_VALUE_REGEX = /^(["'])(.*)\1$/; + +/** Strip a leading locale and surrounding slashes: "/en/a/b/" -> "a/b". */ +export function toContentRelativePath(urlPath: string): string { + return urlPath + .replace(LOCALE_PREFIX_REGEX, "") + .replace(LEADING_SLASH_REGEX, "") + .replace(TRAILING_SLASH_REGEX, ""); +} + +/** + * Absolute file paths that would serve a URL, in order of preference. + * Accepts "/en/a/b", "/a/b", or "a/b". + */ +export function contentFileCandidates(urlPath: string): string[] { + const rel = toContentRelativePath(urlPath); + if (!rel) { + return [join(CONTENT_ROOT, "index.mdx"), join(CONTENT_ROOT, "index.md")]; + } + const base = join(CONTENT_ROOT, rel); + return [ + `${base}.mdx`, + join(base, "index.mdx"), + `${base}.md`, + join(base, "index.md"), + ]; +} + +/** The content file that serves a URL, or null when none exists. */ +export function resolveContentFile(urlPath: string): string | null { + return contentFileCandidates(urlPath).find((p) => existsSync(p)) ?? null; +} + +/** True when an MDX page serves the URL. */ +export function contentPageExists(urlPath: string): boolean { + return resolveContentFile(urlPath) !== null; +} + +/** + * Convert a content file path (absolute or repo-relative) to its URL path + * with the `/en` locale prefix, e.g. "content/docs/build/tools.mdx" -> "/en/build/tools". + */ +export function contentFileToUrl(filePath: string): string { + const rel = ( + filePath.startsWith(sep) || filePath.startsWith("/") + ? relative(CONTENT_ROOT, filePath) + : relative(CONTENT_ROOT_RELATIVE, filePath) + ) + .split(sep) + .join("/") + .replace(INDEX_FILE_REGEX, "") + .replace(MDX_EXTENSION_REGEX, ""); + return rel ? `${LOCALE_PREFIX}/${rel}` : LOCALE_PREFIX; +} + +/** Every MDX/MD content file, as repo-relative paths. */ +export async function listContentFiles(): Promise<string[]> { + return await fg(`${CONTENT_ROOT_RELATIVE}/**/*.{md,mdx}`, { + cwd: process.cwd(), + }); +} + +/** + * Child page slugs of a content directory: `<slug>.mdx` files (excluding + * `index`) plus sub-directories. Mirrors what a `meta.json` `pages` entry can + * refer to. + */ +export function listChildSlugs(dir: string): string[] { + if (!existsSync(dir)) { + return []; + } + const slugs = new Set<string>(); + for (const entry of readdirSync(dir, { withFileTypes: true })) { + if (entry.name.startsWith(".")) { + continue; + } + if (entry.isDirectory()) { + slugs.add(entry.name); + continue; + } + if (MDX_EXTENSION_REGEX.test(entry.name)) { + const slug = entry.name.replace(MDX_EXTENSION_REGEX, ""); + if (slug !== "index") { + slugs.add(slug); + } + } + } + return [...slugs].sort(); +} + +export type MetaJson = { + icon?: string; + pages?: string[]; + title?: string; + [key: string]: unknown; +}; + +/** Parse `<dir>/meta.json`; returns null when it does not exist. */ +export function readMetaJson(dir: string): MetaJson | null { + const file = join(dir, "meta.json"); + if (!existsSync(file)) { + return null; + } + return JSON.parse(readFileSync(file, "utf-8")) as MetaJson; +} + +/** The `pages` array of `<dir>/meta.json` (empty when absent). */ +export function readMetaPages(dir: string): string[] { + return readMetaJson(dir)?.pages ?? []; +} + +const LINK_ENTRY_REGEX = /^(?:\[[^\]]*\])?\[([^\]]*)\]\(([^)]*)\)$/; +const SEPARATOR_ENTRY_REGEX = /^---(.*)---$/; + +/** Parse a `[Title](href)` (or `[Icon][Title](href)`) meta.json entry. */ +export function parseLinkEntry( + entry: string +): { title: string; href: string } | null { + const match = LINK_ENTRY_REGEX.exec(entry); + return match ? { href: match[2], title: match[1] } : null; +} + +/** Parse a `---Title---` meta.json separator entry. */ +export function parseSeparatorEntry(entry: string): { title: string } | null { + const match = SEPARATOR_ENTRY_REGEX.exec(entry); + return match ? { title: match[1] } : null; +} + +/** Raw frontmatter block of an MDX file, or "" when none. */ +export function readFrontmatter(content: string): string { + return FRONTMATTER_REGEX.exec(content)?.[1] ?? ""; +} + +/** Body of an MDX file with the frontmatter block removed. */ +export function stripFrontmatter(content: string): string { + return content.replace(FRONTMATTER_REGEX, ""); +} + +/** + * Read a scalar frontmatter value by key (quoted or bare), or null. + * Good enough for `title`/`description`; not a YAML parser. + */ +export function readFrontmatterValue( + content: string, + key: string +): string | null { + const prefix = `${key}:`; + for (const line of readFrontmatter(content).split("\n")) { + if (!line.startsWith(prefix)) { + continue; + } + const raw = line.slice(prefix.length).trim(); + return raw.replace(QUOTED_VALUE_REGEX, "$2"); + } + return null; +} diff --git a/tests/helpers/fs-source.ts b/tests/helpers/fs-source.ts new file mode 100644 index 000000000..8d15b5278 --- /dev/null +++ b/tests/helpers/fs-source.ts @@ -0,0 +1,89 @@ +/** + * A fumadocs `loader` source built straight from the files under + * `content/docs`, for tests that need the real page tree (URLs, paths, sidebar + * order) without compiling MDX. + * + * `lib/source.ts` feeds `loader()` the generated `.source/server.ts`, which + * imports every page as `*.mdx?collection=docs`. Compiling those in Vitest + * drags each page's component graph into the test process, and some of it + * (for example `@arcadeai/design-system`) touches `document` at module load. + * This helper hands the same `loader` (same `baseUrl`) virtual files read from + * disk instead, so `source.getPages()` yields identical `url`/`path` values. + * + * Use it with `vi.mock("@/lib/source", ...)`: + * + * vi.mock("@/lib/source", async () => { + * const { createFsSource } = await import("./helpers/fs-source"); + * return { source: createFsSource() }; + * }); + */ + +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import fg from "fast-glob"; +import { + loader, + type MetaData, + type PageData, + type VirtualFile, +} from "fumadocs-core/source"; +import { parse as parseYaml } from "yaml"; +import { CONTENT_ROOT } from "./content-paths"; + +const FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---/; + +type Frontmatter = Partial<PageData> & { metaTitle?: string }; + +function readFrontmatter(absolutePath: string): Frontmatter { + const content = readFileSync(absolutePath, "utf-8"); + const raw = FRONTMATTER_REGEX.exec(content)?.[1]; + if (!raw) { + return {}; + } + const parsed: unknown = parseYaml(raw); + return typeof parsed === "object" && parsed !== null + ? (parsed as Frontmatter) + : {}; +} + +/** Virtual files for every `*.mdx` page and `meta.json` under content/docs. */ +export function readContentFiles(): VirtualFile[] { + const pages = fg + .sync("**/*.{md,mdx}", { cwd: CONTENT_ROOT }) + .map((path): VirtualFile => { + const absolutePath = join(CONTENT_ROOT, path); + const frontmatter = readFrontmatter(absolutePath); + return { + absolutePath, + data: { + description: frontmatter.description, + icon: frontmatter.icon, + title: frontmatter.title, + }, + path, + type: "page", + }; + }); + + const metas = fg + .sync("**/meta.json", { cwd: CONTENT_ROOT }) + .map((path): VirtualFile => { + const absolutePath = join(CONTENT_ROOT, path); + return { + absolutePath, + data: JSON.parse(readFileSync(absolutePath, "utf-8")) as MetaData, + path, + type: "meta", + }; + }); + + return [...pages, ...metas]; +} + +/** A `loader()` over the content directory with the same `baseUrl` as lib/source.ts. */ +export function createFsSource() { + return loader({ + baseUrl: "/en", + source: { files: readContentFiles() }, + }); +} diff --git a/tests/integration-category-routes.test.ts b/tests/integration-category-routes.test.ts index 13a77434f..0b77747e7 100644 --- a/tests/integration-category-routes.test.ts +++ b/tests/integration-category-routes.test.ts @@ -1,56 +1,84 @@ -import { existsSync, mkdirSync, mkdtempSync, rmSync } from "node:fs"; +import { + existsSync, + mkdirSync, + mkdtempSync, + rmSync, + writeFileSync, +} from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { describe, expect, test } from "vitest"; +import { describe, expect, test, vi } from "vitest"; +import { matchToolkitRoute } from "@/lib/toolkit-route"; import { INTEGRATION_CATEGORIES } from "@/toolkit-docs-generator/src/shared/toolkit-primitives"; +import { CONTENT_ROOT } from "./helpers/content-paths"; -const INTEGRATIONS_APP_DIR = join( - process.cwd(), - "app", - "en", +// lib/toolkit-route.tsx also exports the page renderer, which pulls the +// toolkit UI (browser-only at module scope) and the fumadocs source into the +// module graph. Only the pure `matchToolkitRoute` is under test here. +vi.mock("@/components/toolkit-docs", () => ({ ToolkitPage: () => null })); +vi.mock("@/lib/source", () => ({ source: { getPage: vi.fn() } })); + +const INTEGRATIONS_CONTENT_DIR = join( + CONTENT_ROOT, "resources", "integrations" ); /** - * normalizeCategory (app/_lib/toolkit-static-params.ts) trusts that every - * value in INTEGRATION_CATEGORIES has a real `[toolkitId]` route directory - * to route toolkits into. If a category is ever added to that list without - * the matching directory (or a directory is removed/renamed), toolkits in - * that category become clickable catalog cards pointing at a route that - * 404s — the same class of bug the "others" catch-all used to hide, since - * tests/integration-index-links.test.ts derives its notion of "valid link" - * from the same normalizeCategory output and can't see this gap. + * normalizeCategory (lib/toolkit-static-params.ts) trusts that every value in + * INTEGRATION_CATEGORIES is a real category: the toolkit route + * (lib/toolkit-route.tsx) must accept it, and the sidebar must have a + * generated `content/docs/resources/integrations/<category>/meta.json` (from + * toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts) so the category's + * toolkits are reachable from navigation. If a category is ever added to that + * list without the matching nav file (or a file is removed/renamed), toolkits + * in that category become clickable catalog cards pointing at a route no + * sidebar links to — the same class of bug the "others" catch-all used to + * hide, since tests/integration-index-links.test.ts derives its notion of + * "valid link" from the same normalizeCategory output and can't see this gap. */ -const missingCategoryDirs = (baseDir: string): string[] => +const missingCategoryMeta = (baseDir: string): string[] => INTEGRATION_CATEGORIES.filter( - (category) => !existsSync(join(baseDir, category, "[toolkitId]")) + (category) => !existsSync(join(baseDir, category, "meta.json")) ); -describe("integration category route directories", () => { - test("every INTEGRATION_CATEGORIES value has a matching [toolkitId] route directory", () => { - expect(missingCategoryDirs(INTEGRATIONS_APP_DIR)).toEqual([]); +describe("integration category routes", () => { + test("every INTEGRATION_CATEGORIES value is accepted by the toolkit route", () => { + for (const category of INTEGRATION_CATEGORIES) { + expect( + matchToolkitRoute(["resources", "integrations", category, "example"]) + ).toEqual({ category, toolkitId: "example" }); + } + }); + + test("the toolkit route ignores non-toolkit slugs", () => { + expect(matchToolkitRoute(undefined)).toBeNull(); + expect(matchToolkitRoute(["resources", "integrations"])).toBeNull(); + expect(matchToolkitRoute(["build", "tools", "x", "y"])).toBeNull(); + }); + + test("every INTEGRATION_CATEGORIES value has a generated sidebar meta.json", () => { + expect(missingCategoryMeta(INTEGRATIONS_CONTENT_DIR)).toEqual([]); }); - test("the check fails when a category's route directory is missing", () => { + test("the check fails when a category's meta.json is missing", () => { // Proves the check above actually catches drift, without touching any // tracked directory: build a scratch tree with every category present, // then remove one and confirm it's flagged. const scratchDir = mkdtempSync(join(tmpdir(), "integration-categories-")); try { for (const category of INTEGRATION_CATEGORIES) { - mkdirSync(join(scratchDir, category, "[toolkitId]"), { - recursive: true, - }); + mkdirSync(join(scratchDir, category), { recursive: true }); + writeFileSync( + join(scratchDir, category, "meta.json"), + JSON.stringify({ pages: [] }) + ); } - const removedCategory = INTEGRATION_CATEGORIES[0]; - rmSync(join(scratchDir, removedCategory, "[toolkitId]"), { - recursive: true, - force: true, - }); + const [removedCategory] = INTEGRATION_CATEGORIES; + rmSync(join(scratchDir, removedCategory, "meta.json"), { force: true }); - expect(missingCategoryDirs(scratchDir)).toEqual([removedCategory]); + expect(missingCategoryMeta(scratchDir)).toEqual([removedCategory]); } finally { rmSync(scratchDir, { recursive: true, force: true }); } diff --git a/tests/integration-index-links.test.ts b/tests/integration-index-links.test.ts index d52763f68..72cf6e800 100644 --- a/tests/integration-index-links.test.ts +++ b/tests/integration-index-links.test.ts @@ -1,25 +1,30 @@ -import { existsSync, readdirSync } from "node:fs"; +import { readdirSync } from "node:fs"; import { readFile } from "node:fs/promises"; import { join } from "node:path"; import { beforeAll, describe, expect, test } from "vitest"; -import { getToolkitsWithDocsLinks } from "@/app/_lib/integration-catalog"; +import { getToolkitsWithDocsLinks } from "@/lib/integration-catalog"; import { type ResolvedIndexToolkit, resolveIndexToolkits, toIntegrationLink, -} from "@/app/_lib/integration-index"; -import { readToolkitData } from "@/app/_lib/toolkit-data"; -import type { ToolkitWithDocsLink } from "@/app/_lib/toolkit-slug"; +} from "@/lib/integration-index"; +import { readToolkitData } from "@/lib/toolkit-data"; +import type { ToolkitWithDocsLink } from "@/lib/toolkit-slug"; import { getToolkitCanonicalPath, listToolkitRoutes, listValidIntegrationLinks, -} from "@/app/_lib/toolkit-static-params"; +} from "@/lib/toolkit-static-params"; import { getToolkitSlug, INTEGRATION_CATEGORIES, } from "@/toolkit-docs-generator/src/shared/toolkit-primitives"; import { redirects } from "../redirects"; +import { + CONTENT_ROOT, + contentPageExists, + listChildSlugs, +} from "./helpers/content-paths"; const TIMEOUT = 30_000; const ROOT = process.cwd(); @@ -157,31 +162,21 @@ describe("integrations index links (live data)", () => { }); // Independently enumerate authored static pages under each integration category -// (a `<category>/<slug>/page.{mdx,tsx}` that isn't the dynamic [toolkitId] route). +// (a `content/docs/resources/integrations/<category>/<slug>.mdx` or +// `<slug>/index.mdx`; toolkit pages have no file and are served by +// lib/toolkit-route.tsx). const listStaticCategoryPageLinks = (): string[] => { const links: string[] = []; for (const category of INTEGRATION_CATEGORIES) { const categoryDir = join( - ROOT, - "app", - "en", + CONTENT_ROOT, "resources", "integrations", category ); - if (!existsSync(categoryDir)) { - continue; - } - for (const entry of readdirSync(categoryDir, { withFileTypes: true })) { - if (!entry.isDirectory() || entry.name.startsWith("[")) { - continue; - } - const pageDir = join(categoryDir, entry.name); - if ( - existsSync(join(pageDir, "page.mdx")) || - existsSync(join(pageDir, "page.tsx")) - ) { - links.push(`${INTEGRATIONS}/${category}/${entry.name}`); + for (const slug of listChildSlugs(categoryDir)) { + if (contentPageExists(`${INTEGRATIONS}/${category}/${slug}`)) { + links.push(`${INTEGRATIONS}/${category}/${slug}`); } } } @@ -197,17 +192,8 @@ const withEnLocale = (path: string): string => const toLocaleParam = (path: string): string => withEnLocale(path).replace(LOCALE_PREFIX, "/:locale"); -const pageFileExists = (path: string): boolean => { - // withEnLocale guarantees a leading "/en"; drop the leading slash to get an - // app-relative path like "en/resources/integrations". - const rel = withEnLocale(path).slice(1); - const base = join(ROOT, "app", rel); - return ( - existsSync(join(base, "page.mdx")) || - existsSync(join(base, "page.tsx")) || - existsSync(`${base}.mdx`) - ); -}; +const pageFileExists = (path: string): boolean => + contentPageExists(withEnLocale(path)); const readRedirectSources = (): Set<string> => new Set(redirects.map((redirect) => redirect.source)); @@ -222,8 +208,8 @@ const extractInternalHrefs = async (relPath: string): Promise<string[]> => { // Component files whose hardcoded internal links are not covered by the // MDX-only broken-link scan. const COMPONENT_FILES = [ - "app/_components/toolkit-docs/components/toolkit-page.tsx", - "app/en/resources/integrations/components/toolkits-client.tsx", + "components/toolkit-docs/components/toolkit-page.tsx", + "components/pages/integrations/toolkits-client.tsx", ]; describe("hardcoded internal links in toolkit components resolve", () => { @@ -273,7 +259,7 @@ describe("hardcoded internal links in toolkit components resolve", () => { // page uses (listToolkitRoutes + readToolkitData + getToolkitSlug) rather than // importing the page module, which pulls in browser-only render code. // -// (The docs sitemap — app/sitemap.ts, static MDX pages only — is guarded in +// (The docs sitemap — app/sitemap.ts, MDX pages plus toolkit routes — is guarded in // tests/sitemap.test.ts: no redirect-source URLs, no duplicates.) // --------------------------------------------------------------------------- @@ -287,24 +273,26 @@ describe("toolkit page canonical hygiene", () => { listValidIntegrationLinks(), readRedirectSources(), ]); - canonicals = []; // Fetch the route list ONCE. getToolkitStaticParamsForCategory() recomputes // listToolkitRoutes() (toolkit index + every data file) internally, so // looping it over all categories re-read the whole catalog once per category. // listToolkitRoutes() already yields both category and toolkitId. - for (const { category, toolkitId } of await listToolkitRoutes()) { - const data = await readToolkitData(toolkitId); - const canonical = data - ? `${INTEGRATIONS}/${category}/${getToolkitSlug({ - id: data.id, - docsLink: data.metadata?.docsLink, - })}` - : ""; - canonicals.push({ - page: `${INTEGRATIONS}/${category}/${toolkitId}`, - canonical, - }); - } + const routes = await listToolkitRoutes(); + canonicals = await Promise.all( + routes.map(async ({ category, toolkitId }) => { + const data = await readToolkitData(toolkitId); + const canonical = data + ? `${INTEGRATIONS}/${category}/${getToolkitSlug({ + docsLink: data.metadata?.docsLink, + id: data.id, + })}` + : ""; + return { + canonical, + page: `${INTEGRATIONS}/${category}/${toolkitId}`, + }; + }) + ); }, TIMEOUT); test("every generated toolkit page emits a canonical", () => { @@ -353,7 +341,7 @@ describe("toolkit page canonical hygiene", () => { expect(offenders).toEqual([]); }); - // MARTECH-17: the dynamic [toolkitId] route accepts ANY category segment, so a + // MARTECH-17: the toolkit route (lib/toolkit-route.tsx) accepts ANY category segment, so a // toolkit is reachable at wrong-category aliases (a docsLink/category mismatch // produced development/pagerduty-api). generateMetadata canonicalizes every // such page to getToolkitCanonicalPath(data) — the toolkit's own category + @@ -368,17 +356,20 @@ describe("toolkit page canonical hygiene", () => { (file) => file.endsWith(".json") && file !== "index.json" ); const orphans: string[] = []; - for (const file of files) { - const parsed = JSON.parse( - await readFile(join(dataDir, file), "utf-8") - ) as { - id?: string; - metadata?: { - category?: string; - docsLink?: string; - isHidden?: boolean; - }; - }; + const parsedFiles = await Promise.all( + files.map(async (file) => ({ + file, + parsed: JSON.parse(await readFile(join(dataDir, file), "utf-8")) as { + id?: string; + metadata?: { + category?: string; + docsLink?: string; + isHidden?: boolean; + }; + }, + })) + ); + for (const { file, parsed } of parsedFiles) { if (!parsed.id || parsed.metadata?.isHidden) { continue; } diff --git a/tests/mdx-page-metadata.test.ts b/tests/mdx-page-metadata.test.ts deleted file mode 100644 index c9d182ca9..000000000 --- a/tests/mdx-page-metadata.test.ts +++ /dev/null @@ -1,246 +0,0 @@ -import { existsSync, readFileSync } from "node:fs"; -import { readdir } from "node:fs/promises"; -import { join, relative } from "node:path"; -import type { NextConfig } from "next"; -import { createElement } from "react"; -import { renderToStaticMarkup } from "react-dom/server"; -import { describe, expect, test } from "vitest"; -import { stripMarkdownFromSwcPageExtensions } from "../lib/swc-page-extensions"; - -type WebpackContext = Parameters<NonNullable<NextConfig["webpack"]>>[1]; - -/** - * Regression coverage for the Next 16.2 SWC app-entry check that broke every - * MDX page (https://github.com/shuding/nextra/issues/5003). - * - * Next 16.2 builds the "is this a page/layout/route?" regex inside the SWC - * react-server-components transform out of `nextConfig.pageExtensions`. Because - * Nextra puts "md"/"mdx" in there so its pages route, `page.mdx` started - * reaching a validator that rejects the `export const metadata` Nextra emits, - * and the whole site failed to compile. lib/swc-page-extensions.ts keeps the - * markdown extensions out of the copy handed to next-swc-loader. - * - * These tests pin both halves of that trade: markdown still routes, and the - * generated pages still carry server-resolved metadata. - */ - -const APP_DIR = join(process.cwd(), "app", "en"); -const BUILD_DIR = join(process.cwd(), ".next", "server", "app"); - -const SWC_LOADER = "/next/dist/build/webpack/loaders/next-swc-loader.js"; -const NON_SWC_LOADER = "/next/dist/build/webpack/loaders/next-flight-loader.js"; - -const FRONTMATTER_TITLE_RE = /^title:\s*["']?(.+?)["']?\s*$/m; -const HTML_TITLE_RE = /<title>([^<]*)<\/title>/; -const FRONTMATTER_DESCRIPTION_RE = /^description:\s*\S/m; -const PAGE_MDX_SUFFIX_RE = /\/page\.mdx$/; -const CSS_TEST_RE = /\.css$/; - -function makeSwcRule(pageExtensions: string[]) { - return { - loader: SWC_LOADER, - options: { nextConfig: { pageExtensions: [...pageExtensions] } }, - }; -} - -function pageExtensionsOf(rule: { - options: { nextConfig: { pageExtensions: string[] } }; -}) { - return rule.options.nextConfig.pageExtensions; -} - -async function findPageFiles(dir: string): Promise<string[]> { - const entries = await readdir(dir, { withFileTypes: true }); - const found: string[] = []; - for (const entry of entries) { - const full = join(dir, entry.name); - if (entry.isDirectory()) { - found.push(...(await findPageFiles(full))); - } else if (entry.name === "page.mdx") { - found.push(full); - } - } - return found; -} - -/** `app/en/build/arcade-deploy/page.mdx` -> `.next/server/app/en/build/arcade-deploy.html` */ -function prerenderedHtmlFor(pageFile: string): string { - const route = relative(join(process.cwd(), "app"), pageFile).replace( - PAGE_MDX_SUFFIX_RE, - "" - ); - return join(BUILD_DIR, `${route}.html`); -} - -describe("stripMarkdownFromSwcPageExtensions", () => { - test("drops markdown extensions from next-swc-loader options", () => { - const rule = makeSwcRule(["js", "jsx", "ts", "tsx", "md", "mdx"]); - - stripMarkdownFromSwcPageExtensions([rule]); - - expect(pageExtensionsOf(rule)).toEqual(["js", "jsx", "ts", "tsx"]); - }); - - test("leaves loaders other than next-swc-loader alone", () => { - const rule = { - loader: NON_SWC_LOADER, - options: { nextConfig: { pageExtensions: ["ts", "mdx"] } }, - }; - - stripMarkdownFromSwcPageExtensions([rule]); - - expect(pageExtensionsOf(rule)).toEqual(["ts", "mdx"]); - }); - - test("reaches loaders nested under use, oneOf, and rules", () => { - const inUse = makeSwcRule(["ts", "mdx"]); - const inOneOf = makeSwcRule(["ts", "md"]); - const inRules = makeSwcRule(["ts", "mdx"]); - - stripMarkdownFromSwcPageExtensions([ - { use: [inUse] }, - { oneOf: [{ use: [inOneOf] }] }, - { rules: [inRules] }, - ]); - - expect(pageExtensionsOf(inUse)).toEqual(["ts"]); - expect(pageExtensionsOf(inOneOf)).toEqual(["ts"]); - expect(pageExtensionsOf(inRules)).toEqual(["ts"]); - }); - - test("replaces the nextConfig object instead of mutating the shared one", () => { - // Next hands the same nextConfig object to every loader entry, and routing - // still reads its markdown extensions. Mutating it in place would unroute - // every MDX page. - const shared = { pageExtensions: ["ts", "mdx"] }; - const rule = { loader: SWC_LOADER, options: { nextConfig: shared } }; - - stripMarkdownFromSwcPageExtensions([rule]); - - expect(shared.pageExtensions).toEqual(["ts", "mdx"]); - expect(rule.options.nextConfig).not.toBe(shared); - expect(rule.options.nextConfig.pageExtensions).toEqual(["ts"]); - }); - - test("ignores rules with no pageExtensions to strip", () => { - const rules = [ - { loader: SWC_LOADER }, - { test: CSS_TEST_RE }, - null, - "string", - ]; - - expect(() => stripMarkdownFromSwcPageExtensions(rules)).not.toThrow(); - }); -}); - -describe("next.config.ts", () => { - test("still routes .md and .mdx pages", async () => { - const { default: nextConfig } = await import("../next.config"); - - expect(nextConfig.pageExtensions).toContain("md"); - expect(nextConfig.pageExtensions).toContain("mdx"); - }); - - test("webpack hook strips markdown from what next-swc-loader sees", async () => { - const { default: nextConfig } = await import("../next.config"); - const swcRule = makeSwcRule(nextConfig.pageExtensions ?? []); - // Only the fields Nextra's and our own webpack hooks actually touch. - const webpackConfig = { - watchOptions: {}, - resolve: { alias: {}, extensionAlias: {} }, - module: { rules: [swcRule] as unknown[] }, - plugins: [], - }; - const context: WebpackContext = { - dir: process.cwd(), - dev: false, - isServer: true, - buildId: "test", - config: nextConfig as WebpackContext["config"], - defaultLoaders: { babel: {} }, - totalPages: 0, - webpack: {}, - }; - - nextConfig.webpack?.(webpackConfig, context); - - expect(pageExtensionsOf(swcRule)).not.toContain("md"); - expect(pageExtensionsOf(swcRule)).not.toContain("mdx"); - expect(pageExtensionsOf(swcRule)).toContain("tsx"); - // Routing must keep them. - expect(nextConfig.pageExtensions).toContain("mdx"); - }); -}); - -describe("MDX pages", () => { - test("every page.mdx that declares a frontmatter title declares a description too", async () => { - const pages = await findPageFiles(APP_DIR); - - expect(pages.length).toBeGreaterThan(0); - - const missing = pages.filter((page) => { - const source = readFileSync(page, "utf8"); - if (!FRONTMATTER_TITLE_RE.test(source)) { - return false; - } - return !FRONTMATTER_DESCRIPTION_RE.test(source); - }); - - expect(missing.map((page) => relative(process.cwd(), page))).toEqual([]); - }); - - // Only meaningful against a real build; `pnpm build` populates .next/server/app. - describe.skipIf(!existsSync(BUILD_DIR))("prerendered output", () => { - test("renders a title for every MDX page", async () => { - const pages = await findPageFiles(APP_DIR); - const built = pages - .map((page) => ({ page, html: prerenderedHtmlFor(page) })) - .filter(({ html }) => existsSync(html)); - - expect(built.length).toBeGreaterThan(0); - - const untitled = built.filter(({ html }) => { - const title = readFileSync(html, "utf8").match(HTML_TITLE_RE)?.[1]; - return !title?.trim(); - }); - - expect(untitled.map(({ page }) => relative(process.cwd(), page))).toEqual( - [] - ); - }); - - test("resolves frontmatter metadata on the server", async () => { - // The Next 16.2 validator claimed these pages were client components, - // which would have dropped `metadata` entirely. Prove it survives into - // the prerendered HTML. - const pages = await findPageFiles(APP_DIR); - const mismatches: string[] = []; - - for (const page of pages) { - const html = prerenderedHtmlFor(page); - if (!existsSync(html)) { - continue; - } - const frontmatterTitle = readFileSync(page, "utf8").match( - FRONTMATTER_TITLE_RE - )?.[1]; - if (!frontmatterTitle) { - continue; - } - const renderedTitle = - readFileSync(html, "utf8").match(HTML_TITLE_RE)?.[1] ?? ""; - const escapedTitle = renderToStaticMarkup( - createElement("title", null, frontmatterTitle) - ).match(HTML_TITLE_RE)?.[1]; - if (!(escapedTitle && renderedTitle.includes(escapedTitle))) { - mismatches.push( - `${relative(process.cwd(), page)}: expected "${frontmatterTitle}" in "${renderedTitle}"` - ); - } - } - - expect(mismatches).toEqual([]); - }); - }); -}); diff --git a/tests/neutralize-emails.test.tsx b/tests/neutralize-emails.test.tsx index 54c16d664..d8d56eecd 100644 --- a/tests/neutralize-emails.test.tsx +++ b/tests/neutralize-emails.test.tsx @@ -5,7 +5,7 @@ import { rehypeNeutralizeEmails, splitEmails, splitEmailText, -} from "@/app/_components/toolkit-docs/lib/neutralize-emails"; +} from "@/components/toolkit-docs/lib/neutralize-emails"; /** * MARTECH-17: Cloudflare's Email Obfuscation rewrites any contiguous email-like diff --git a/tests/page-size.test.ts b/tests/page-size.test.ts index 089c3da89..4029aad8f 100644 --- a/tests/page-size.test.ts +++ b/tests/page-size.test.ts @@ -1,8 +1,8 @@ import { readdirSync, readFileSync } from "node:fs"; import { join } from "node:path"; import { describe, expect, test } from "vitest"; -import type { ToolkitData } from "@/app/_components/toolkit-docs/types"; -import { toToolkitSummary } from "@/app/_lib/toolkit-data"; +import type { ToolkitData } from "@/components/toolkit-docs/types"; +import { toToolkitSummary } from "@/lib/toolkit-data"; /** * MARTECH-17: auto-generated toolkit reference pages exceeded Googlebot's 2 MB diff --git a/tests/partner-integration-nav.test.ts b/tests/partner-integration-nav.test.ts index 83fef3cab..006d2cede 100644 --- a/tests/partner-integration-nav.test.ts +++ b/tests/partner-integration-nav.test.ts @@ -1,9 +1,17 @@ -import { existsSync } from "node:fs"; +import { readFileSync } from "node:fs"; import { join } from "node:path"; -import type { MetaRecord } from "nextra"; import { describe, expect, test } from "vitest"; -import { PARTNER_TOOLKITS } from "@/app/_data/partner-toolkits"; +import { PARTNER_TOOLKITS } from "@/data/partner-toolkits"; import { getToolkitSlug } from "@/toolkit-docs-generator/src/shared/toolkit-primitives"; +import { + CONTENT_ROOT, + contentPageExists, + parseLinkEntry, + parseSeparatorEntry, + readFrontmatterValue, + readMetaPages, + resolveContentFile, +} from "./helpers/content-paths"; /** * Partner integrations are hand-authored pages that no toolkit JSON file backs, @@ -14,14 +22,14 @@ import { getToolkitSlug } from "@/toolkit-docs-generator/src/shared/toolkit-prim * here instead of shipping a sidebar link to a 404. * * The sidebar entries themselves are generated (see - * toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts), so a missing entry - * below means someone hand-edited a `_meta.tsx` and skipped the sync, or the - * sync ran against a partner list the committed nav predates. + * toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts, which emits + * content/docs/resources/integrations/<category>/meta.json), so a missing + * entry below means someone hand-edited a `meta.json` and skipped the sync, or + * the sync ran against a partner list the committed nav predates. */ -const INTEGRATIONS_DIR = join(process.cwd(), "app/en/resources/integrations"); +const INTEGRATIONS_DIR = join(CONTENT_ROOT, "resources", "integrations"); const INTEGRATIONS_BASE_PATH = "/en/resources/integrations"; -const PAGE_FILE_NAMES = ["page.mdx", "page.tsx"]; const partnerCases = PARTNER_TOOLKITS.map((partner) => ({ partner, @@ -31,9 +39,35 @@ const partnerCases = PARTNER_TOOLKITS.map((partner) => ({ }), })); -const loadCategoryMeta = async (category: string): Promise<MetaRecord> => { - const meta = await import(join(INTEGRATIONS_DIR, category, "_meta.tsx")); - return meta.default as MetaRecord; +const loadCategoryPages = (category: string): string[] => + readMetaPages(join(INTEGRATIONS_DIR, category)); + +/** + * The label a meta.json entry renders for a partner page. Generated toolkit + * entries are `[Label](href)` links; partner pages are real MDX files, so the + * sync emits their bare slug and Fumadocs reads the label from the page's + * frontmatter (`sidebarTitle`, else `title`). + */ +const sidebarLabelFor = ( + pages: string[], + slug: string, + href: string +): string | undefined => { + if (pages.includes(slug)) { + const file = resolveContentFile(href); + if (!file) { + return; + } + const content = readFileSync(file, "utf-8"); + return ( + readFrontmatterValue(content, "sidebarTitle") ?? + readFrontmatterValue(content, "title") ?? + undefined + ); + } + return pages + .map(parseLinkEntry) + .find((entry) => entry !== null && entry.href === href)?.title; }; describe("partner integrations", () => { @@ -44,37 +78,34 @@ describe("partner integrations", () => { test.each(partnerCases)( "$partner.id has a page on disk", ({ partner, slug }) => { - const pageDir = join(INTEGRATIONS_DIR, partner.category, slug); - const hasPage = PAGE_FILE_NAMES.some((fileName) => - existsSync(join(pageDir, fileName)) - ); + const url = `${INTEGRATIONS_BASE_PATH}/${partner.category}/${slug}`; expect( - hasPage, - `Expected a page for partner "${partner.id}" at ${pageDir}/page.mdx` + contentPageExists(url), + `Expected a page for partner "${partner.id}" at content/docs/resources/integrations/${partner.category}/${slug}.mdx (or ${slug}/index.mdx)` ).toBe(true); } ); test.each(partnerCases)( "$partner.id has a sidebar entry pointing at its page", - async ({ partner, slug }) => { - const meta = await loadCategoryMeta(partner.category); - const entry = meta[slug]; + ({ partner, slug }) => { + const pages = loadCategoryPages(partner.category); + const href = `${INTEGRATIONS_BASE_PATH}/${partner.category}/${slug}`; + const label = sidebarLabelFor(pages, slug, href); expect( - entry, - `Expected a "${slug}" key in app/en/resources/integrations/${partner.category}/_meta.tsx. ` + - "Run `npx tsx toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts` to regenerate it." + label, + `Expected a "${slug}" (or "[${partner.label}](${href})") entry in content/docs/resources/integrations/${partner.category}/meta.json. ` + + "Run `bun run sync-toolkit-sidebar` to regenerate it." ).toBeDefined(); - expect(entry).toMatchObject({ - title: partner.label, - href: `${INTEGRATIONS_BASE_PATH}/${partner.category}/${slug}`, - }); - expect(meta["-- Partners"]).toMatchObject({ - type: "separator", - title: "Partners", - }); + expect(label).toBe(partner.label); + + const separators = pages + .map(parseSeparatorEntry) + .filter((separator) => separator !== null) + .map((separator) => separator.title); + expect(separators).toContain("Partners"); } ); diff --git a/tests/root-files.test.ts b/tests/root-files.test.ts new file mode 100644 index 000000000..ba0a2bb5f --- /dev/null +++ b/tests/root-files.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, it } from "vitest"; +import { config as proxyConfig } from "@/proxy"; +import { redirects } from "@/redirects"; + +const LOCALE_ROOT_SOURCE = /^\/:locale(\([^)]*\))?$/; +const SCOPED_LOCALE_ROOT = /^\/:locale\(en\|es\|pt-BR\)$/; + +/** + * Root-level files (`/llms.txt`, `/robots.txt`, `/sitemap.xml`) must never be + * caught by the locale redirect or the locale proxy. A bare + * `/:locale` redirect source matched every single-segment path and sent + * `/llms.txt` to `/llms.txt/home`. + */ +describe("root files stay reachable", () => { + it("any locale-root redirect only matches real locales", () => { + // The locale root itself is redirected by proxy.ts, as in production. + const localeRoot = redirects.filter((r) => + LOCALE_ROOT_SOURCE.test(r.source) + ); + for (const r of localeRoot) { + expect(r.source).toMatch(SCOPED_LOCALE_ROOT); + } + }); + + it("the proxy matcher excludes text files", () => { + const matcher = proxyConfig.matcher.join(" "); + for (const ext of ["txt", "xml", "json"]) { + expect(matcher).toContain(`\\.${ext}$`); + } + }); +}); diff --git a/tests/scripts/check-redirects-utils.test.ts b/tests/scripts/check-redirects-utils.test.ts index 405d8caee..f51bcfa59 100644 --- a/tests/scripts/check-redirects-utils.test.ts +++ b/tests/scripts/check-redirects-utils.test.ts @@ -1,99 +1,134 @@ +/** + * Unit tests for scripts/lib/check-redirects-utils.ts. + * + * These cover the pure path/URL helpers behind `bun run check-redirects`, so + * the mapping between `content/docs/**` files and `/:locale/...` URLs is + * pinned down without shelling out to git or touching the real filesystem. + */ + import { describe, expect, it, vi } from "vitest"; import { + CONTENT_FILE_REGEX, checkWildcardMatch, - type DynamicRouteMove, dynamicFileToUrlPattern, dynamicRouteExists, fileToUrl, isMoveCoveredByRedirect, pageExists, parseDynamicRouteMoves, - type Redirect, + TOOLKIT_ROUTE_MODULE, urlToFile, + urlToFileCandidates, } from "../../scripts/lib/check-redirects-utils"; +describe("CONTENT_FILE_REGEX", () => { + it("matches content/docs markdown files only", () => { + expect(CONTENT_FILE_REGEX.test("content/docs/guides/foo.mdx")).toBe(true); + expect(CONTENT_FILE_REGEX.test("content/docs/guides/foo/index.mdx")).toBe( + true + ); + expect(CONTENT_FILE_REGEX.test("content/docs/guides/foo.md")).toBe(true); + expect(CONTENT_FILE_REGEX.test("content/docs/guides/meta.json")).toBe( + false + ); + expect(CONTENT_FILE_REGEX.test("components/foo.tsx")).toBe(false); + expect(CONTENT_FILE_REGEX.test("README.md")).toBe(false); + }); +}); + describe("fileToUrl", () => { - it("converts file path to URL path", () => { - expect(fileToUrl("app/en/guides/foo/page.mdx")).toBe("/:locale/guides/foo"); + it("converts a flat content file to a locale URL", () => { + expect(fileToUrl("content/docs/guides/foo.mdx")).toBe( + "/:locale/guides/foo" + ); + }); + + it("converts a folder index file to the folder URL", () => { + expect(fileToUrl("content/docs/guides/foo/index.mdx")).toBe( + "/:locale/guides/foo" + ); }); it("handles nested paths", () => { expect( - fileToUrl("app/en/resources/integrations/productivity/gmail/page.mdx") + fileToUrl("content/docs/resources/integrations/productivity/gmail.mdx") ).toBe("/:locale/resources/integrations/productivity/gmail"); }); - it("handles root page", () => { - expect(fileToUrl("app/en/page.mdx")).toBe("/:locale"); + it("maps the root index page to the bare locale", () => { + expect(fileToUrl("content/docs/index.mdx")).toBe("/:locale"); }); - it("handles .md extension", () => { - expect(fileToUrl("app/en/guides/foo/page.md")).toBe("/:locale/guides/foo"); + it("handles .md files", () => { + expect(fileToUrl("content/docs/guides/foo.md")).toBe("/:locale/guides/foo"); }); - it("handles different locales", () => { - expect(fileToUrl("app/pt/guides/foo/page.mdx")).toBe("/:locale/guides/foo"); + it("does not treat a page named index-like as the index", () => { + expect(fileToUrl("content/docs/guides/index-page.mdx")).toBe( + "/:locale/guides/index-page" + ); }); }); -describe("urlToFile", () => { - it("converts URL path to file path", () => { - expect(urlToFile("/:locale/guides/foo")).toBe("app/en/guides/foo/page.mdx"); +describe("urlToFile / urlToFileCandidates", () => { + it("prefers the flat <path>.mdx form", () => { + expect(urlToFile("/:locale/guides/foo")).toBe( + "content/docs/guides/foo.mdx" + ); + }); + + it("lists the flat file, the folder index, and their .md variants", () => { + expect(urlToFileCandidates("/:locale/guides/foo")).toEqual([ + "content/docs/guides/foo.mdx", + "content/docs/guides/foo/index.mdx", + "content/docs/guides/foo.md", + "content/docs/guides/foo/index.md", + ]); }); it("handles nested paths", () => { expect( urlToFile("/:locale/resources/integrations/productivity/gmail") - ).toBe("app/en/resources/integrations/productivity/gmail/page.mdx"); + ).toBe("content/docs/resources/integrations/productivity/gmail.mdx"); }); - it("handles root URL", () => { - expect(urlToFile("/:locale")).toBe("app/en/page.mdx"); - expect(urlToFile("/:locale/")).toBe("app/en/page.mdx"); + it("maps the bare locale to the root index page", () => { + expect(urlToFile("/:locale")).toBe("content/docs/index.mdx"); + expect(urlToFile("/:locale/")).toBe("content/docs/index.mdx"); }); }); describe("dynamicFileToUrlPattern", () => { - it("converts dynamic route to URL pattern", () => { + it("converts [param] to :param", () => { expect( dynamicFileToUrlPattern( - "app/en/resources/integrations/productivity/[toolkitId]/page.mdx" + "content/docs/resources/integrations/productivity/[toolkitId].mdx" ) ).toBe("/:locale/resources/integrations/productivity/:toolkitId"); }); - it("handles catch-all routes", () => { - expect(dynamicFileToUrlPattern("app/en/docs/[...slug]/page.mdx")).toBe( - "/:locale/docs/:slug*" - ); + it("converts [...param] to :param*", () => { + expect( + dynamicFileToUrlPattern("content/docs/docs/[...slug]/index.mdx") + ).toBe("/:locale/docs/:slug*"); }); it("handles multiple dynamic segments", () => { - expect(dynamicFileToUrlPattern("app/en/[category]/[id]/page.mdx")).toBe( - "/:locale/:category/:id" - ); + expect( + dynamicFileToUrlPattern("content/docs/[category]/[id]/index.mdx") + ).toBe("/:locale/:category/:id"); }); - it("handles static paths", () => { - expect(dynamicFileToUrlPattern("app/en/about/page.mdx")).toBe( + it("returns a plain URL when there are no dynamic segments", () => { + expect(dynamicFileToUrlPattern("content/docs/about.mdx")).toBe( "/:locale/about" ); }); - - it("handles root with dynamic segment", () => { - expect(dynamicFileToUrlPattern("app/en/[slug]/page.mdx")).toBe( - "/:locale/:slug" - ); - }); }); describe("dynamicRouteExists", () => { - it("returns true when dynamic route exists", () => { - const mockExists = vi.fn( - (path: string) => - path === - "app/en/resources/integrations/productivity/[toolkitId]/page.mdx" - ); + it("recognizes a toolkit URL when the toolkit route module exists", () => { + const mockExists = vi.fn((path: string) => path === TOOLKIT_ROUTE_MODULE); expect( dynamicRouteExists( @@ -101,154 +136,127 @@ describe("dynamicRouteExists", () => { mockExists ) ).toBe(true); + expect(mockExists).toHaveBeenCalledWith("lib/toolkit-route.tsx"); }); - it("returns false when no dynamic route exists", () => { - const mockExists = vi.fn(() => false); - + it("returns false for a toolkit URL when the route module is missing", () => { expect( - dynamicRouteExists("/:locale/some/nonexistent/path", mockExists) + dynamicRouteExists( + "/:locale/resources/integrations/productivity/gmail", + () => false + ) ).toBe(false); }); - it("checks multiple dynamic patterns", () => { - const checkedPaths: string[] = []; - const mockExists = vi.fn((path: string) => { - checkedPaths.push(path); - return false; - }); - - dynamicRouteExists("/:locale/docs/page", mockExists); - - // Should check [toolkitId], [slug], [id], [...slug] for each segment - expect(checkedPaths).toContain("app/en/docs/[toolkitId]/page.mdx"); - expect(checkedPaths).toContain("app/en/docs/[slug]/page.mdx"); - expect(checkedPaths).toContain("app/en/[toolkitId]/page/page.mdx"); - }); - - it("also checks .md extension", () => { - const mockExists = vi.fn( - (path: string) => path === "app/en/docs/[slug]/page.md" - ); - - expect(dynamicRouteExists("/:locale/docs/intro", mockExists)).toBe(true); - }); - - it("returns true when nested dynamic route exists", () => { - const mockExists = vi.fn((path: string) => { - // The function checks [toolkitId], [slug], [id], [...slug] patterns - return path === "app/en/[slug]/test/page.mdx"; - }); - - // It should check multiple positions, including replacing the first segment - expect(dynamicRouteExists("/:locale/guides/test", mockExists)).toBe(true); + it("returns false for URLs outside resources/integrations/<category>/<id>", () => { + const always = () => true; + expect(dynamicRouteExists("/:locale/guides/foo", always)).toBe(false); + expect( + dynamicRouteExists("/:locale/resources/integrations/productivity", always) + ).toBe(false); + expect( + dynamicRouteExists( + "/:locale/resources/integrations/productivity/gmail/extra", + always + ) + ).toBe(false); }); }); describe("pageExists", () => { - it("returns true for paths with :path* wildcard", () => { - const mockExists = vi.fn(() => false); - expect(pageExists("/:locale/api/:path*", mockExists)).toBe(true); - expect(mockExists).not.toHaveBeenCalled(); + it("returns true for wildcard paths", () => { + expect(pageExists("/:locale/guides/:path*", () => false)).toBe(true); + expect(pageExists("/:locale/guides/:path", () => false)).toBe(true); }); - it("returns true for paths with :path parameter", () => { - const mockExists = vi.fn(() => false); - expect(pageExists("/:locale/docs/:path", mockExists)).toBe(true); - }); - - it("returns true when static page exists", () => { + it("returns true when the flat .mdx file exists", () => { const mockExists = vi.fn( - (path: string) => path === "app/en/about/page.mdx" + (path: string) => path === "content/docs/about.mdx" ); - expect(pageExists("/:locale/about", mockExists)).toBe(true); }); - it("returns true when .md page exists", () => { - const mockExists = vi.fn((path: string) => path === "app/en/about/page.md"); - + it("returns true when the folder index.mdx exists", () => { + const mockExists = vi.fn( + (path: string) => path === "content/docs/about/index.mdx" + ); expect(pageExists("/:locale/about", mockExists)).toBe(true); }); - it("returns true when dynamic route serves the path", () => { + it("returns true when the .md file exists", () => { const mockExists = vi.fn( - (path: string) => path === "app/en/resources/[toolkitId]/page.mdx" + (path: string) => path === "content/docs/about.md" ); + expect(pageExists("/:locale/about", mockExists)).toBe(true); + }); - expect(pageExists("/:locale/resources/gmail", mockExists)).toBe(true); + it("falls back to the toolkit route for toolkit URLs", () => { + const mockExists = vi.fn((path: string) => path === TOOLKIT_ROUTE_MODULE); + expect( + pageExists( + "/:locale/resources/integrations/development/github", + mockExists + ) + ).toBe(true); }); - it("returns false when no page serves the path", () => { - const mockExists = vi.fn(() => false); - expect(pageExists("/:locale/nonexistent", mockExists)).toBe(false); + it("returns false when nothing matches", () => { + expect(pageExists("/:locale/nonexistent", () => false)).toBe(false); }); }); describe("parseDynamicRouteMoves", () => { - it("detects renamed dynamic route files", () => { + it("detects a renamed dynamic route content file", () => { const gitDiff = - "R100\tapp/en/old/[toolkitId]/page.mdx\tapp/en/new/[toolkitId]/page.mdx"; - + "R100\tcontent/docs/old/[toolkitId].mdx\tcontent/docs/new/[toolkitId].mdx"; const moves = parseDynamicRouteMoves(gitDiff); expect(moves).toHaveLength(1); expect(moves[0]).toEqual({ - oldPath: "app/en/old/[toolkitId]/page.mdx", - newPath: "app/en/new/[toolkitId]/page.mdx", + newPath: "content/docs/new/[toolkitId].mdx", + oldPath: "content/docs/old/[toolkitId].mdx", oldUrl: "/:locale/old/:toolkitId", newUrl: "/:locale/new/:toolkitId", }); }); - it("ignores moves where URL pattern stays the same", () => { - // Renaming [id] to [toolkitId] in same location doesn't change URL + it("detects a changed parameter name", () => { const gitDiff = - "R100\tapp/en/resources/[id]/page.mdx\tapp/en/resources/[toolkitId]/page.mdx"; - + "R100\tcontent/docs/resources/[id].mdx\tcontent/docs/resources/[toolkitId].mdx"; const moves = parseDynamicRouteMoves(gitDiff); - // Both result in /:locale/resources/:id and /:locale/resources/:toolkitId - // These are different, so it should be detected expect(moves).toHaveLength(1); + expect(moves[0].oldUrl).toBe("/:locale/resources/:id"); + expect(moves[0].newUrl).toBe("/:locale/resources/:toolkitId"); }); - it("ignores non-page files", () => { + it("ignores non-content renames", () => { const gitDiff = - "R100\tapp/en/old/[toolkitId]/index.ts\tapp/en/new/[toolkitId]/index.ts"; - - const moves = parseDynamicRouteMoves(gitDiff); - - expect(moves).toHaveLength(0); + "R100\tcomponents/old/[toolkitId]/index.ts\tcomponents/new/[toolkitId]/index.ts"; + expect(parseDynamicRouteMoves(gitDiff)).toHaveLength(0); }); - it("ignores deleted files (D status)", () => { - const gitDiff = "D\tapp/en/old/[toolkitId]/page.mdx"; - - const moves = parseDynamicRouteMoves(gitDiff); - - expect(moves).toHaveLength(0); + it("ignores deletions", () => { + const gitDiff = "D\tcontent/docs/old/[toolkitId].mdx"; + expect(parseDynamicRouteMoves(gitDiff)).toHaveLength(0); }); - it("handles multiple moves", () => { + it("handles multiple renames", () => { const gitDiff = [ - "R100\tapp/en/old1/[id]/page.mdx\tapp/en/new1/[id]/page.mdx", - "R095\tapp/en/old2/[slug]/page.mdx\tapp/en/new2/[slug]/page.mdx", + "R100\tcontent/docs/old1/[id].mdx\tcontent/docs/new1/[id].mdx", + "R095\tcontent/docs/old2/[slug].mdx\tcontent/docs/new2/[slug].mdx", + "M\tcontent/docs/other.mdx", ].join("\n"); - const moves = parseDynamicRouteMoves(gitDiff); expect(moves).toHaveLength(2); + expect(moves[0].oldUrl).toBe("/:locale/old1/:id"); + expect(moves[1].oldUrl).toBe("/:locale/old2/:slug"); }); - it("handles empty output", () => { - expect(parseDynamicRouteMoves("")).toHaveLength(0); - }); - - it("detects when static page moves to dynamic route location", () => { + it("detects a static page moving under a dynamic route", () => { const gitDiff = - "R100\tapp/en/tools/gmail/page.mdx\tapp/en/tools/[toolkitId]/page.mdx"; - + "R100\tcontent/docs/tools/gmail.mdx\tcontent/docs/tools/[toolkitId].mdx"; const moves = parseDynamicRouteMoves(gitDiff); expect(moves).toHaveLength(1); @@ -258,94 +266,59 @@ describe("parseDynamicRouteMoves", () => { }); describe("isMoveCoveredByRedirect", () => { - const move: DynamicRouteMove = { - oldPath: "app/en/old/[toolkitId]/page.mdx", - newPath: "app/en/new/[toolkitId]/page.mdx", + const move = { + newPath: "content/docs/new/[toolkitId].mdx", + oldPath: "content/docs/old/[toolkitId].mdx", oldUrl: "/:locale/old/:toolkitId", newUrl: "/:locale/new/:toolkitId", }; - it("returns true for exact match", () => { - const redirects: Redirect[] = [ - { - source: "/:locale/old/:toolkitId", - destination: "/:locale/new/:toolkitId", - }, - ]; - - expect(isMoveCoveredByRedirect(move, redirects)).toBe(true); + it("matches an exact source", () => { + expect( + isMoveCoveredByRedirect(move, [ + { destination: "/:locale/new", source: "/:locale/old/:toolkitId" }, + ]) + ).toBe(true); }); - it("returns true when wildcard covers the path", () => { - const redirects: Redirect[] = [ - { source: "/:locale/old/:path*", destination: "/:locale/new/:path*" }, - ]; - - expect(isMoveCoveredByRedirect(move, redirects)).toBe(true); + it("matches a wildcard covering the old path", () => { + expect( + isMoveCoveredByRedirect(move, [ + { destination: "/:locale/new/:path*", source: "/:locale/old/:path*" }, + ]) + ).toBe(true); }); it("returns false when no redirect covers the move", () => { - const redirects: Redirect[] = [ - { - source: "/:locale/other/:path*", - destination: "/:locale/somewhere/:path*", - }, - ]; - - expect(isMoveCoveredByRedirect(move, redirects)).toBe(false); - }); - - it("returns false for empty redirects", () => { - expect(isMoveCoveredByRedirect(move, [])).toBe(false); - }); - - it("handles multiple redirects", () => { - const redirects: Redirect[] = [ - { source: "/:locale/unrelated", destination: "/:locale/other" }, - { - source: "/:locale/old/:toolkitId", - destination: "/:locale/new/:toolkitId", - }, - ]; - - expect(isMoveCoveredByRedirect(move, redirects)).toBe(true); + expect( + isMoveCoveredByRedirect(move, [ + { destination: "/:locale/x/:path*", source: "/:locale/other/:path*" }, + ]) + ).toBe(false); }); }); describe("checkWildcardMatch", () => { - it("returns true when wildcard prefix matches", () => { - const redirects: Redirect[] = [ - { source: "/:locale/old/:path*", destination: "/:locale/new/:path*" }, - ]; + const redirects = [ + { destination: "/:locale/build/:path*", source: "/:locale/guides/:path*" }, + { destination: "/:locale/other", source: "/:locale/exact" }, + ]; - expect(checkWildcardMatch("/:locale/old/foo/bar", redirects)).toBe(true); + it("matches a path under a wildcard prefix", () => { + expect(checkWildcardMatch("/:locale/guides/foo/bar", redirects)).toBe(true); }); - it("returns true for exact prefix match", () => { - const redirects: Redirect[] = [ - { source: "/:locale/old/:path*", destination: "/:locale/new/:path*" }, - ]; - - expect(checkWildcardMatch("/:locale/old", redirects)).toBe(true); + it("matches the wildcard prefix itself", () => { + expect(checkWildcardMatch("/:locale/guides", redirects)).toBe(true); }); - it("returns false when no wildcard matches", () => { - const redirects: Redirect[] = [ - { source: "/:locale/other/:path*", destination: "/:locale/new/:path*" }, - ]; - - expect(checkWildcardMatch("/:locale/old/foo", redirects)).toBe(false); + it("does not match exact (non-wildcard) redirects", () => { + expect(checkWildcardMatch("/:locale/exact", redirects)).toBe(false); }); - it("ignores non-wildcard redirects", () => { - const redirects: Redirect[] = [ - { source: "/:locale/old", destination: "/:locale/new" }, - ]; - - expect(checkWildcardMatch("/:locale/old/foo", redirects)).toBe(false); - }); - - it("returns false for empty redirects", () => { - expect(checkWildcardMatch("/:locale/anything", [])).toBe(false); + it("does not match a sibling prefix", () => { + expect(checkWildcardMatch("/:locale/guides-old/foo", redirects)).toBe( + false + ); }); }); diff --git a/tests/scripts/generate-llmstxt.test.ts b/tests/scripts/generate-llmstxt.test.ts index c3abaa51e..299ff9708 100644 --- a/tests/scripts/generate-llmstxt.test.ts +++ b/tests/scripts/generate-llmstxt.test.ts @@ -45,7 +45,7 @@ describe("placeholder retry that fails again", () => { const [retried] = await summarizePagesInBatches( [ { - path: "app/en/get-started/quickstart/page.mdx", + path: "content/docs/get-started/quickstart.mdx", url: PAGE_URL, content: "# Quickstart\n\nGet going.", }, diff --git a/tests/scripts/helm-chart-docs.test.ts b/tests/scripts/helm-chart-docs.test.ts index 997f57ca3..b6687017e 100644 --- a/tests/scripts/helm-chart-docs.test.ts +++ b/tests/scripts/helm-chart-docs.test.ts @@ -99,19 +99,22 @@ describe("syncHelmChartDocs", () => { "examples/notes.txt": "skip", }); const docsDir = mkdtempSync(join(tmpdir(), "helm-docs-")); - const result = syncHelmChartDocs({ chartDir, docsDir, version: "1.12.0" }); + const archiveDir = mkdtempSync(join(tmpdir(), "helm-archive-")); + const result = syncHelmChartDocs({ + archiveDir, + chartDir, + docsDir, + version: "1.12.0", + }); expect(result.exampleCount).toBe(1); expect( - readFileSync(join(docsDir, "_chart/1.12.0/values.md"), "utf-8") + readFileSync(join(archiveDir, "1.12.0/values.md"), "utf-8") ).toContain("| a |"); expect( - readFileSync(join(docsDir, "_chart/1.12.0/examples/small.yaml"), "utf-8") + readFileSync(join(archiveDir, "1.12.0/examples/small.yaml"), "utf-8") ).toBe("a: 1\n"); - const page = readFileSync( - join(docsDir, "values-reference/page.mdx"), - "utf-8" - ); + const page = readFileSync(join(docsDir, "values-reference.mdx"), "utf-8"); expect(page).toContain("chart version `1.12.0`"); await expect(compile(page)).resolves.toBeDefined(); }); @@ -119,11 +122,13 @@ describe("syncHelmChartDocs", () => { it("treats zero examples as fine and rejects bad versions", () => { const chartDir = makeChart({ "README.md": README }); const docsDir = mkdtempSync(join(tmpdir(), "helm-docs-")); + const archiveDir = mkdtempSync(join(tmpdir(), "helm-archive-")); expect( - syncHelmChartDocs({ chartDir, docsDir, version: "1.12.0" }).exampleCount + syncHelmChartDocs({ archiveDir, chartDir, docsDir, version: "1.12.0" }) + .exampleCount ).toBe(0); expect(() => - syncHelmChartDocs({ chartDir, docsDir, version: "../x" }) + syncHelmChartDocs({ archiveDir, chartDir, docsDir, version: "../x" }) ).toThrow("Invalid version"); }); }); diff --git a/tests/scripts/update-links-utils.test.ts b/tests/scripts/update-links-utils.test.ts new file mode 100644 index 000000000..630ed3309 --- /dev/null +++ b/tests/scripts/update-links-utils.test.ts @@ -0,0 +1,65 @@ +import { describe, expect, it } from "vitest"; +import { + getUpdatableRedirects, + updateLinksInContent, +} from "@/scripts/lib/update-links-utils"; + +describe("getUpdatableRedirects", () => { + it("keeps plain page redirects", () => { + const result = getUpdatableRedirects([ + { destination: "/:locale/new", source: "/:locale/old" }, + ]); + expect(result).toHaveLength(1); + }); + + it("skips wildcard, parameterised and placeholder redirects", () => { + const result = getUpdatableRedirects([ + { + destination: "/:locale/b/:path*", + source: "/:locale/a/:path*", + }, + { + destination: "/:locale/b/:id", + source: "/:locale/a/:id", + }, + { + destination: "REPLACE_WITH_NEW_PATH", + source: "/:locale/a", + }, + ]); + expect(result).toHaveLength(0); + }); + + it("skips locale-root redirects, whose empty path would match every link and backtick pair", () => { + const result = getUpdatableRedirects([ + { destination: "/:locale/home", source: "/:locale" }, + { destination: "/:locale/home", source: "/:locale/" }, + ]); + expect(result).toHaveLength(0); + }); +}); + +describe("updateLinksInContent", () => { + it("rewrites markdown, JSX and backtick references to the old path", () => { + const input = [ + "See [guide](/old-path) and [section](/old-path#intro).", + '<Link href="/old-path">x</Link>', + "<a href='/old-path#x'>y</a>", + "Visit `/old-path` for details.", + ].join("\n"); + const output = updateLinksInContent(input, "/old-path", "/new-path"); + expect(output).toBe( + [ + "See [guide](/new-path) and [section](/new-path#intro).", + '<Link href="/new-path">x</Link>', + "<a href='/new-path#x'>y</a>", + "Visit `/new-path` for details.", + ].join("\n") + ); + }); + + it("leaves code fences and same-page anchors alone", () => { + const input = "```bash\nls\n```\n\n[top](#top)\n"; + expect(updateLinksInContent(input, "/old-path", "/new-path")).toBe(input); + }); +}); diff --git a/tests/shared-service-domain.test.ts b/tests/shared-service-domain.test.ts index 49e2d8b41..e36443fdb 100644 --- a/tests/shared-service-domain.test.ts +++ b/tests/shared-service-domain.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { getSharedServiceDomain } from "@/app/_components/toolkit-docs/components/toolkit-page-utils"; +import { getSharedServiceDomain } from "@/components/toolkit-docs/components/toolkit-page-utils"; const makeTool = (domains: string[]) => ({ metadata: { diff --git a/tests/sitemap-resilience.test.ts b/tests/sitemap-resilience.test.ts index 0ac022fe9..bb9db4882 100644 --- a/tests/sitemap-resilience.test.ts +++ b/tests/sitemap-resilience.test.ts @@ -2,7 +2,16 @@ import { beforeEach, expect, test, vi } from "vitest"; const listValidIntegrationLinks = vi.fn(); -vi.mock("../app/_lib/toolkit-static-params", () => ({ +// app/sitemap.ts reads the page list from lib/source.ts, whose generated +// `.source` module imports every MDX page (and its components) into the test +// process. Build the same loader from the files on disk instead; see +// tests/helpers/fs-source.ts. +vi.mock("@/lib/source", async () => { + const { createFsSource } = await import("./helpers/fs-source"); + return { source: createFsSource() }; +}); + +vi.mock("../lib/toolkit-static-params", () => ({ listValidIntegrationLinks, })); diff --git a/tests/sitemap.test.ts b/tests/sitemap.test.ts index ab0dc50bc..76db19de8 100644 --- a/tests/sitemap.test.ts +++ b/tests/sitemap.test.ts @@ -1,9 +1,18 @@ import { readFileSync } from "node:fs"; import { readdir, stat } from "node:fs/promises"; import { join } from "node:path"; -import { expect, test } from "vitest"; +import { expect, test, vi } from "vitest"; import { redirects } from "../redirects"; +// app/sitemap.ts reads the page list from lib/source.ts, whose generated +// `.source` module imports every MDX page (and its components) into the test +// process. Build the same loader from the files on disk instead; see +// tests/helpers/fs-source.ts. +vi.mock("@/lib/source", async () => { + const { createFsSource } = await import("./helpers/fs-source"); + return { source: createFsSource() }; +}); + const toolkitDataDir = join( process.cwd(), "toolkit-docs-generator", @@ -33,10 +42,13 @@ test("sitemap lists expected URLs", async () => { "https://example.test/en/references/cli-cheat-sheet" ); - // Generated toolkit pages (served by the `[toolkitId]` dynamic route, - // which the directory walk above can't see) must still make it into the - // sitemap. This fails if the toolkit-route merge in app/sitemap.ts is - // reverted. + // The published sitemap has never listed the API reference page (the old + // directory walk skipped every folder named "api"); keep the URL set equal. + expect(urls).not.toContain("https://example.test/en/references/api"); + + // Generated toolkit pages (served by lib/toolkit-route.tsx, which the + // content page tree can't see) must still make it into the sitemap. This + // fails if the toolkit-route merge in app/sitemap.ts is reverted. expect(urls).toContain( "https://example.test/en/resources/integrations/development/github" ); diff --git a/tests/toolkit-data-cache.test.ts b/tests/toolkit-data-cache.test.ts index dfe7a46ff..7a016560b 100644 --- a/tests/toolkit-data-cache.test.ts +++ b/tests/toolkit-data-cache.test.ts @@ -2,10 +2,10 @@ import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterAll, describe, expect, test, vi } from "vitest"; -import { readToolkitData, readToolkitIndex } from "@/app/_lib/toolkit-data"; +import { readToolkitData, readToolkitIndex } from "@/lib/toolkit-data"; /** - * loadAllToolkitData (app/_lib/toolkit-data.ts) reads and validates every + * loadAllToolkitData (lib/toolkit-data.ts) reads and validates every * toolkit file in a data directory once, then serves all lookups from the * resulting map. That eager read means one corrupt file can no longer be * skipped by requesting a different, healthy toolkit — the whole directory diff --git a/tests/toolkit-data-dir.test.ts b/tests/toolkit-data-dir.test.ts index e68916394..21f4a5bf1 100644 --- a/tests/toolkit-data-dir.test.ts +++ b/tests/toolkit-data-dir.test.ts @@ -21,8 +21,8 @@ afterEach(() => { /** * The default must be anchored on the working directory, not on this module's - * own location. `app/_lib/toolkit-data.ts` imports it into the Next.js server - * bundle, where webpack replaces `import.meta.url` with a compile-time + * own location. `lib/toolkit-data.ts` imports it into the Next.js server + * bundle, where the bundler replaces `import.meta.url` with a compile-time * constant: the source path on the build machine. A self-relative default * therefore resolves to a directory that does not exist inside the deployed * function, and `/api/toolkit-data/[toolkitId]` returns 500 in production while diff --git a/tests/toolkit-data-parity.test.ts b/tests/toolkit-data-parity.test.ts index 870bc29c0..94c5d3a4b 100644 --- a/tests/toolkit-data-parity.test.ts +++ b/tests/toolkit-data-parity.test.ts @@ -1,8 +1,8 @@ import { readdirSync } from "node:fs"; import { join } from "node:path"; import { describe, expect, test } from "vitest"; -import { readToolkitFile, readToolkitIndex } from "@/app/_lib/toolkit-data"; -import { listToolkitRoutes } from "@/app/_lib/toolkit-static-params"; +import { readToolkitFile, readToolkitIndex } from "@/lib/toolkit-data"; +import { listToolkitRoutes } from "@/lib/toolkit-static-params"; import { resolveToolkitDataDir } from "@/toolkit-docs-generator/src/shared/toolkit-data-dir"; import { getToolkitSlug } from "@/toolkit-docs-generator/src/shared/toolkit-primitives"; @@ -28,7 +28,7 @@ describe("toolkit data parity", () => { (file) => file.endsWith(".json") && file !== "index.json" ); - // readToolkitFile throws on a corrupt file (see app/_lib/toolkit-data.ts), + // readToolkitFile throws on a corrupt file (see lib/toolkit-data.ts), // so a bad file fails this test loudly instead of quietly shrinking the // "parseable" count below. const toolkits = await Promise.all( diff --git a/tests/toolkit-data.test.ts b/tests/toolkit-data.test.ts index 84decef5e..6e830b12a 100644 --- a/tests/toolkit-data.test.ts +++ b/tests/toolkit-data.test.ts @@ -3,7 +3,7 @@ import { tmpdir } from "node:os"; import { basename, join } from "node:path"; import { describe, expect, it } from "vitest"; -import { readToolkitData, readToolkitIndex } from "@/app/_lib/toolkit-data"; +import { readToolkitData, readToolkitIndex } from "@/lib/toolkit-data"; const loadFixture = async (fileName: string): Promise<string> => { const fixturesDir = new URL( diff --git a/tests/toolkit-markdown.test.ts b/tests/toolkit-markdown.test.ts index e2a166be1..5ec4b8fec 100644 --- a/tests/toolkit-markdown.test.ts +++ b/tests/toolkit-markdown.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from "vitest"; -import type { ToolkitData } from "@/app/_components/toolkit-docs/types"; -import { toToolkitMarkdown } from "@/app/_lib/toolkit-markdown"; +import type { ToolkitData } from "@/components/toolkit-docs/types"; +import { toToolkitMarkdown } from "@/lib/toolkit-markdown"; /** * MARTECH-17 follow-up: toolkit pages render per-tool detail client-only, so the diff --git a/tests/toolkit-static-params.test.ts b/tests/toolkit-static-params.test.ts index 6b8e92b0a..fc0dca82b 100644 --- a/tests/toolkit-static-params.test.ts +++ b/tests/toolkit-static-params.test.ts @@ -6,7 +6,7 @@ import { getToolkitStaticParamsForCategory, listToolkitRoutes, type ToolkitCatalogEntry, -} from "@/app/_lib/toolkit-static-params"; +} from "@/lib/toolkit-static-params"; import { normalizeToolkitId } from "@/toolkit-docs-generator/src/shared/toolkit-primitives"; const INVALID_TOOLKIT_INDEX_SCHEMA_ERROR = /Invalid toolkit index schema/; diff --git a/toolkit-docs-generator/.cursorrules b/toolkit-docs-generator/.cursorrules index bb9de7b58..c7e5cd8f4 100644 --- a/toolkit-docs-generator/.cursorrules +++ b/toolkit-docs-generator/.cursorrules @@ -82,9 +82,9 @@ Always validate output against Zod schemas before writing files. Before committing changes, ALWAYS run: ```bash -pnpm lint # Check code style -pnpm typecheck # Verify types -pnpm test # Run all tests +bun run lint # Check code style +bun run typecheck # Verify types +bun run test # Run all tests ``` Or use make commands from the root docs project: diff --git a/toolkit-docs-generator/ARCHITECTURE.md b/toolkit-docs-generator/ARCHITECTURE.md index c1cf255a8..65e0350e6 100644 --- a/toolkit-docs-generator/ARCHITECTURE.md +++ b/toolkit-docs-generator/ARCHITECTURE.md @@ -36,8 +36,8 @@ flowchart TD report --> slack["Slack alert"] json --> verify["Verify output"] - verify --> sidebar["Sync sidebar _meta.tsx"] - sidebar --> llms["pnpm llmstxt<br/>regenerates public/llms.txt"] + verify --> sidebar["Sync sidebar meta.json"] + sidebar --> llms["bun run llmstxt<br/>regenerates public/llms.txt"] llms --> pr["Pull request on<br/>automation/toolkit-docs"] pr -->|"a human merges"| main["main"] main --> vercel["Vercel: next build"] @@ -121,11 +121,12 @@ If you need HTML output, add a separate build step in the app. The generator int The generator does not run during a Vercel build. The generation workflow commits the JSON files and navigation changes through a pull request. Vercel then runs the -root `pnpm build` command and compiles the committed files with Next.js. +root `bun run build` command and compiles the committed files with Next.js. -`app/_lib/toolkit-static-params.ts` enumerates routes from `index.json` and the -per-toolkit files. `app/_lib/toolkit-data.ts` reads the same files for page -rendering and the `/api/toolkit-data/[toolkitId]` route. The root layout no +`lib/toolkit-static-params.ts` enumerates routes from `index.json` and the +per-toolkit files. `lib/toolkit-data.ts` reads the same files for page +rendering (through `lib/toolkit-route.tsx`, the catch-all route's fallback for +non-MDX slugs) and the `/api/toolkit-data/[toolkitId]` route. The root layout no longer reads request headers — the locale it needs is a hardcoded constant, since `proxy.ts` redirects every request to an `/en` path — so Vercel can statically render the toolkit routes at build time from the committed JSON. @@ -134,7 +135,7 @@ statically render the toolkit routes at build time from the committed JSON. Search uses an external Algolia crawler. There is no Pagefind or local search index build step in this repository. After deployment, the crawler indexes the -rendered site. `app/_components/algolia-search.tsx` queries that index with the +rendered site. `components/algolia-search.tsx` queries that index with the public, read-only values configured through these Vercel environment variables: - `NEXT_PUBLIC_ALGOLIA_APP_ID` diff --git a/toolkit-docs-generator/CURATION.md b/toolkit-docs-generator/CURATION.md index f92a1b671..3e42eaff9 100644 --- a/toolkit-docs-generator/CURATION.md +++ b/toolkit-docs-generator/CURATION.md @@ -10,7 +10,7 @@ Behavior below comes from `src/sources/markdown-curation.ts` (compiles the directory), `src/shared/toolkit-schemas.ts` (schemas), `src/merger/data-merger.ts` (attaches curation to toolkits and tools), and, in the app, `toolkit-page.tsx`, `tool-section.tsx`, and -`documentation-chunk-renderer.tsx` under `app/_components/toolkit-docs/`. +`documentation-chunk-renderer.tsx` under `components/toolkit-docs/`. ## Directory layout @@ -232,8 +232,8 @@ detectable offline. ```bash cd toolkit-docs-generator -../node_modules/.bin/tsx src/cli/index.ts validate-curation -../node_modules/.bin/tsx src/cli/index.ts validate-curation --toolkit GoogleFlights +bunx tsx src/cli/index.ts validate-curation +bunx tsx src/cli/index.ts validate-curation --toolkit GoogleFlights ``` This needs no credentials. It reports per-toolkit counts, prints each failing @@ -241,15 +241,14 @@ toolkit with its message, and exits non-zero on failure. It runs the same compiler generation uses, so a pass means kinds, frontmatter, and MDX are all valid. It cannot check `tool:` targets. -Invoke `tsx` by path because `toolkit-docs-generator` has no package for -`pnpm exec`. +`bun` runs TypeScript directly, so no `tsx` install is needed. Seeing a chunk on a page needs generated JSON. Either wait for the automated generation PR, or generate that one toolkit with Engine credentials and run -`pnpm dev` from the repo root: +`bun run dev` from the repo root: ```bash -../node_modules/.bin/tsx src/cli/index.ts generate \ +bunx tsx src/cli/index.ts generate \ --providers "GoogleFlights" \ --api-url "$ARCADE_API_URL" \ --custom-sections ./curation \ diff --git a/toolkit-docs-generator/README.md b/toolkit-docs-generator/README.md index e68f55799..ba759799e 100644 --- a/toolkit-docs-generator/README.md +++ b/toolkit-docs-generator/README.md @@ -39,7 +39,7 @@ It runs these steps: 1. Type-check and test the toolkit docs generator. 2. Generate toolkit JSON using `toolkit-docs-generator` and the public tool catalog. -3. Sync sidebar navigation from `toolkit-docs-generator/data/toolkits` to the `_meta.tsx` files. +3. Sync sidebar navigation from `toolkit-docs-generator/data/toolkits` to the category `meta.json` files. 4. Create or update a pull request if there are changes. Required secrets: @@ -57,11 +57,11 @@ Optional secrets: The docs site consumes the generated JSON directly: -- `app/_lib/toolkit-data.ts` reads `toolkit-docs-generator/data/toolkits/<toolkit>.json`. -- `app/_lib/toolkit-static-params.ts` builds routes from `index.json` and the design system catalog. -- `app/_components/toolkit-docs` renders the toolkit page using `metadata.iconUrl`, `metadata.type`, and auth fields. -- `app/en/resources/integrations/preview` uses the design system catalog for preview pages. -- `app/en/resources/integrations/*/_meta.tsx` controls sidebar navigation for each category. +- `lib/toolkit-data.ts` reads `toolkit-docs-generator/data/toolkits/<toolkit>.json`. +- `lib/toolkit-static-params.ts` builds routes from `index.json` and the design system catalog. +- `components/toolkit-docs` renders the toolkit page using `metadata.iconUrl`, `metadata.type`, and auth fields. +- `lib/toolkit-route.tsx` serves every `/en/resources/integrations/<category>/<toolkit>` page from the JSON when no MDX page matches (the docs catch-all route falls back to it). +- `content/docs/resources/integrations/<category>/meta.json` controls sidebar navigation for each category (Fumadocs folder meta). ## Sidebar sync step (required for navigation) @@ -69,7 +69,7 @@ The docs site consumes the generated JSON directly: - Reads `toolkit-docs-generator/data/toolkits` for available toolkits. - Uses the design system for category and label, with JSON label fallback. -- Groups toolkits by category and writes `_meta.tsx` files per category. +- Groups toolkits by category and writes a Fumadocs `meta.json` per category. - Uses a fixed category order and alphabetical label order within each nav group. - Splits `optimized` vs `starter` using `metadata.type` (or an `*Api` fallback). - Does not prune categories unless you pass `--prune` (not used in the workflow). @@ -114,7 +114,7 @@ The summary generator is configured to **never list OAuth scopes** in the genera ### Local example (editor on) ```bash -pnpm dlx tsx src/cli/index.ts generate \ +bunx tsx src/cli/index.ts generate \ --providers "Github" \ --api-source public-catalog \ --llm-provider openai \ @@ -137,9 +137,9 @@ pnpm dlx tsx src/cli/index.ts generate \ ## Local usage -Run these commands from the `toolkit-docs-generator` directory. Invoke `tsx` by -path because this directory has no package for `pnpm exec`; the sidebar sync -command below runs from the repo root instead. +Run these commands from the `toolkit-docs-generator` directory with `bun`, +which executes TypeScript directly (no `tsx` needed); the sidebar sync command +below runs from the repo root instead. `--api-source public-catalog` is spelled out below because a bare run with no catalog or engine environment variables falls back to the mock fixtures. @@ -147,7 +147,7 @@ catalog or engine environment variables falls back to the mock fixtures. Generate a single toolkit: ```bash -pnpm dlx tsx src/cli/index.ts generate \ +bunx tsx src/cli/index.ts generate \ --providers "Github:1.0.0" \ --api-source public-catalog \ --llm-provider openai \ @@ -159,7 +159,7 @@ pnpm dlx tsx src/cli/index.ts generate \ Generate all toolkits: ```bash -pnpm dlx tsx src/cli/index.ts generate \ +bunx tsx src/cli/index.ts generate \ --all \ --skip-unchanged \ --api-source public-catalog \ @@ -172,7 +172,7 @@ pnpm dlx tsx src/cli/index.ts generate \ Generate without LLM output: ```bash -pnpm dlx tsx src/cli/index.ts generate \ +bunx tsx src/cli/index.ts generate \ --providers "Asana:0.1.3" \ --api-source public-catalog \ --skip-examples \ @@ -183,7 +183,7 @@ pnpm dlx tsx src/cli/index.ts generate \ Sync sidebar navigation locally: ```bash -pnpm dlx tsx toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts +bun toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts ``` ## Ignoring toolkits @@ -200,7 +200,7 @@ SomeInternalTool ``` ```bash -pnpm dlx tsx src/cli/index.ts generate --all \ +bunx tsx src/cli/index.ts generate --all \ --ignore-file ./skip-toolkits.txt ``` @@ -227,7 +227,7 @@ SomeInternalTool The parser ignores blank lines and lines starting with `#`. IDs are case-insensitive. ```bash -pnpm dlx tsx src/cli/index.ts generate --all \ +bunx tsx src/cli/index.ts generate --all \ --exclude-file ./remove-toolkits.txt ``` @@ -269,7 +269,7 @@ which `location` and `position` combinations actually render, and the full list of failure messages. Before writing a curation file, check your work with: ```bash -pnpm dlx tsx src/cli/index.ts validate-curation --toolkit GoogleFlights +bunx tsx src/cli/index.ts validate-curation --toolkit GoogleFlights ``` That compiles the directory with the same code generation uses and needs no @@ -287,7 +287,7 @@ from it. Two outcomes, and the difference matters: Each entry names the reason and the fix. Every run attaches the full report as its `failed-tools` artifact. Build the message locally against any report with -`pnpm dlx tsx src/cli/index.ts alert --report <file>`. It prints nothing when a +`bunx tsx src/cli/index.ts alert --report <file>`. It prints nothing when a run has nothing to report. No red run stays quiet. Three messages can arrive, and they answer different @@ -309,4 +309,4 @@ message and the third, because those are two separate things to fix. - **Nothing regenerated**: `--skip-unchanged` exits early when tool definitions did not change. - **Missing metadata**: when the catalog returns `metadata: null` for a toolkit the merger substitutes defaults and warns. Re-run once the experience API's design-system read recovers, or use `--require-complete` to fail instead of publishing defaults. -- **Verify output fails**: run `pnpm dlx tsx src/cli/index.ts verify-output --output data/toolkits` and fix the reported mismatch. +- **Verify output fails**: run `bunx tsx src/cli/index.ts verify-output --output data/toolkits` and fix the reported mismatch. diff --git a/toolkit-docs-generator/curation/airtableapi/imports/001.mdx b/toolkit-docs-generator/curation/airtableapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/airtableapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/airtableapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/asanaapi/imports/001.mdx b/toolkit-docs-generator/curation/asanaapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/asanaapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/asanaapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/ashbyapi/imports/001.mdx b/toolkit-docs-generator/curation/ashbyapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/ashbyapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/ashbyapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/boxapi/imports/001.mdx b/toolkit-docs-generator/curation/boxapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/boxapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/boxapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/calendlyapi/imports/001.mdx b/toolkit-docs-generator/curation/calendlyapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/calendlyapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/calendlyapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/clickupapi/imports/001.mdx b/toolkit-docs-generator/curation/clickupapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/clickupapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/clickupapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/cursoragentsapi/imports/001.mdx b/toolkit-docs-generator/curation/cursoragentsapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/cursoragentsapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/cursoragentsapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/customerioapi/imports/001.mdx b/toolkit-docs-generator/curation/customerioapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/customerioapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/customerioapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/customeriopipelinesapi/imports/001.mdx b/toolkit-docs-generator/curation/customeriopipelinesapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/customeriopipelinesapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/customeriopipelinesapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/customeriotrackapi/imports/001.mdx b/toolkit-docs-generator/curation/customeriotrackapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/customeriotrackapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/customeriotrackapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/datadogapi/imports/001.mdx b/toolkit-docs-generator/curation/datadogapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/datadogapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/datadogapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/exaapi/imports/001.mdx b/toolkit-docs-generator/curation/exaapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/exaapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/exaapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/figmaapi/imports/001.mdx b/toolkit-docs-generator/curation/figmaapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/figmaapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/figmaapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/freshserviceapi/imports/001.mdx b/toolkit-docs-generator/curation/freshserviceapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/freshserviceapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/freshserviceapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/github/imports/001.mdx b/toolkit-docs-generator/curation/github/imports/001.mdx index 7fd9ba2fa..9a37566db 100644 --- a/toolkit-docs-generator/curation/github/imports/001.mdx +++ b/toolkit-docs-generator/curation/github/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import { Callout, Tabs } from "nextra/components"; +import { Callout } from "@/components/mdx/callout"; diff --git a/toolkit-docs-generator/curation/github/imports/002.mdx b/toolkit-docs-generator/curation/github/imports/002.mdx new file mode 100644 index 000000000..aba640b7a --- /dev/null +++ b/toolkit-docs-generator/curation/github/imports/002.mdx @@ -0,0 +1,4 @@ +--- +type: import +--- +import { Tabs } from "@/components/mdx/tabs"; diff --git a/toolkit-docs-generator/curation/githubapi/imports/001.mdx b/toolkit-docs-generator/curation/githubapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/githubapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/githubapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/gmail/imports/001.mdx b/toolkit-docs-generator/curation/gmail/imports/001.mdx index 6826321ed..01dac6295 100644 --- a/toolkit-docs-generator/curation/gmail/imports/001.mdx +++ b/toolkit-docs-generator/curation/gmail/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import ScopePicker from "@/app/_components/scope-picker"; +import ScopePicker from "@/components/scope-picker"; diff --git a/toolkit-docs-generator/curation/googlecalendar/imports/001.mdx b/toolkit-docs-generator/curation/googlecalendar/imports/001.mdx index 6826321ed..01dac6295 100644 --- a/toolkit-docs-generator/curation/googlecalendar/imports/001.mdx +++ b/toolkit-docs-generator/curation/googlecalendar/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import ScopePicker from "@/app/_components/scope-picker"; +import ScopePicker from "@/components/scope-picker"; diff --git a/toolkit-docs-generator/curation/googlecontacts/imports/001.mdx b/toolkit-docs-generator/curation/googlecontacts/imports/001.mdx index 6826321ed..01dac6295 100644 --- a/toolkit-docs-generator/curation/googlecontacts/imports/001.mdx +++ b/toolkit-docs-generator/curation/googlecontacts/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import ScopePicker from "@/app/_components/scope-picker"; +import ScopePicker from "@/components/scope-picker"; diff --git a/toolkit-docs-generator/curation/googledocs/imports/001.mdx b/toolkit-docs-generator/curation/googledocs/imports/001.mdx index 6826321ed..01dac6295 100644 --- a/toolkit-docs-generator/curation/googledocs/imports/001.mdx +++ b/toolkit-docs-generator/curation/googledocs/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import ScopePicker from "@/app/_components/scope-picker"; +import ScopePicker from "@/components/scope-picker"; diff --git a/toolkit-docs-generator/curation/googledrive/imports/001.mdx b/toolkit-docs-generator/curation/googledrive/imports/001.mdx index 6826321ed..01dac6295 100644 --- a/toolkit-docs-generator/curation/googledrive/imports/001.mdx +++ b/toolkit-docs-generator/curation/googledrive/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import ScopePicker from "@/app/_components/scope-picker"; +import ScopePicker from "@/components/scope-picker"; diff --git a/toolkit-docs-generator/curation/googlesheets/imports/001.mdx b/toolkit-docs-generator/curation/googlesheets/imports/001.mdx index 6826321ed..01dac6295 100644 --- a/toolkit-docs-generator/curation/googlesheets/imports/001.mdx +++ b/toolkit-docs-generator/curation/googlesheets/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import ScopePicker from "@/app/_components/scope-picker"; +import ScopePicker from "@/components/scope-picker"; diff --git a/toolkit-docs-generator/curation/googleslides/imports/001.mdx b/toolkit-docs-generator/curation/googleslides/imports/001.mdx index 6826321ed..01dac6295 100644 --- a/toolkit-docs-generator/curation/googleslides/imports/001.mdx +++ b/toolkit-docs-generator/curation/googleslides/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import ScopePicker from "@/app/_components/scope-picker"; +import ScopePicker from "@/components/scope-picker"; diff --git a/toolkit-docs-generator/curation/hubspotautomationapi/imports/001.mdx b/toolkit-docs-generator/curation/hubspotautomationapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/hubspotautomationapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/hubspotautomationapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/hubspotcmsapi/imports/001.mdx b/toolkit-docs-generator/curation/hubspotcmsapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/hubspotcmsapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/hubspotcmsapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/hubspotconversationsapi/imports/001.mdx b/toolkit-docs-generator/curation/hubspotconversationsapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/hubspotconversationsapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/hubspotconversationsapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/hubspotcrmapi/imports/001.mdx b/toolkit-docs-generator/curation/hubspotcrmapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/hubspotcrmapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/hubspotcrmapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/hubspoteventsapi/imports/001.mdx b/toolkit-docs-generator/curation/hubspoteventsapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/hubspoteventsapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/hubspoteventsapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/hubspotmarketingapi/imports/001.mdx b/toolkit-docs-generator/curation/hubspotmarketingapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/hubspotmarketingapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/hubspotmarketingapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/hubspotmeetingsapi/imports/001.mdx b/toolkit-docs-generator/curation/hubspotmeetingsapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/hubspotmeetingsapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/hubspotmeetingsapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/hubspotusersapi/imports/001.mdx b/toolkit-docs-generator/curation/hubspotusersapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/hubspotusersapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/hubspotusersapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/intercomapi/imports/001.mdx b/toolkit-docs-generator/curation/intercomapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/intercomapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/intercomapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/jira/pages/environment-variables/page.mdx b/toolkit-docs-generator/curation/jira/pages/environment-variables/page.mdx index 6ddda8cbf..3234f692d 100644 --- a/toolkit-docs-generator/curation/jira/pages/environment-variables/page.mdx +++ b/toolkit-docs-generator/curation/jira/pages/environment-variables/page.mdx @@ -1,7 +1,7 @@ --- type: environment-variables --- -import { Callout } from "nextra/components"; +import { Callout } from "@/components/mdx/callout"; # Jira Environment Variables diff --git a/toolkit-docs-generator/curation/lumaapi/imports/001.mdx b/toolkit-docs-generator/curation/lumaapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/lumaapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/lumaapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/mailchimpmarketingapi/imports/001.mdx b/toolkit-docs-generator/curation/mailchimpmarketingapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/mailchimpmarketingapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/mailchimpmarketingapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/miroapi/imports/001.mdx b/toolkit-docs-generator/curation/miroapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/miroapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/miroapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/pagerdutyapi/imports/001.mdx b/toolkit-docs-generator/curation/pagerdutyapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/pagerdutyapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/pagerdutyapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/posthogapi/imports/001.mdx b/toolkit-docs-generator/curation/posthogapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/posthogapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/posthogapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/slackapi/imports/001.mdx b/toolkit-docs-generator/curation/slackapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/slackapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/slackapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/squareupapi/imports/001.mdx b/toolkit-docs-generator/curation/squareupapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/squareupapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/squareupapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/stripeapi/imports/001.mdx b/toolkit-docs-generator/curation/stripeapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/stripeapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/stripeapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/ticktickapi/imports/001.mdx b/toolkit-docs-generator/curation/ticktickapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/ticktickapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/ticktickapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/trelloapi/imports/001.mdx b/toolkit-docs-generator/curation/trelloapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/trelloapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/trelloapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/vercelapi/imports/001.mdx b/toolkit-docs-generator/curation/vercelapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/vercelapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/vercelapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/weaviateapi/imports/001.mdx b/toolkit-docs-generator/curation/weaviateapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/weaviateapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/weaviateapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/xeroapi/imports/001.mdx b/toolkit-docs-generator/curation/xeroapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/xeroapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/xeroapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/zohobooksapi/imports/001.mdx b/toolkit-docs-generator/curation/zohobooksapi/imports/001.mdx index d325b093d..d4bd4eb78 100644 --- a/toolkit-docs-generator/curation/zohobooksapi/imports/001.mdx +++ b/toolkit-docs-generator/curation/zohobooksapi/imports/001.mdx @@ -1,4 +1,4 @@ --- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; diff --git a/toolkit-docs-generator/curation/zoom/pages/install/page.mdx b/toolkit-docs-generator/curation/zoom/pages/install/page.mdx index c0fc899f6..cdb2bd8ba 100644 --- a/toolkit-docs-generator/curation/zoom/pages/install/page.mdx +++ b/toolkit-docs-generator/curation/zoom/pages/install/page.mdx @@ -3,8 +3,9 @@ type: install --- # Arcade for Zoom -import { Steps, Callout } from "nextra/components"; -import { SignupLink } from "@/app/_components/analytics"; +import { Steps } from "@/components/mdx/steps"; +import { Callout } from "@/components/mdx/callout"; +import { SignupLink } from "@/components/analytics"; import { ZoomAuthLink } from "./zoom-auth-link"; ## Integrate Arcade with your Zoom account diff --git a/toolkit-docs-generator/data/toolkits/airtableapi.json b/toolkit-docs-generator/data/toolkits/airtableapi.json index de2a02eac..108335555 100644 --- a/toolkit-docs-generator/data/toolkits/airtableapi.json +++ b/toolkit-docs-generator/data/toolkits/airtableapi.json @@ -7170,7 +7170,7 @@ ], "documentationChunks": [], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.054Z", diff --git a/toolkit-docs-generator/data/toolkits/asanaapi.json b/toolkit-docs-generator/data/toolkits/asanaapi.json index 0e615fb18..de04be35c 100644 --- a/toolkit-docs-generator/data/toolkits/asanaapi.json +++ b/toolkit-docs-generator/data/toolkits/asanaapi.json @@ -18786,7 +18786,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-08-10T11:26:15.897Z", diff --git a/toolkit-docs-generator/data/toolkits/ashbyapi.json b/toolkit-docs-generator/data/toolkits/ashbyapi.json index 5fd41581a..094da8c9f 100644 --- a/toolkit-docs-generator/data/toolkits/ashbyapi.json +++ b/toolkit-docs-generator/data/toolkits/ashbyapi.json @@ -8360,7 +8360,7 @@ ], "documentationChunks": [], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-27T11:41:15.692Z", diff --git a/toolkit-docs-generator/data/toolkits/boxapi.json b/toolkit-docs-generator/data/toolkits/boxapi.json index 10e7eb6f3..556855ec0 100644 --- a/toolkit-docs-generator/data/toolkits/boxapi.json +++ b/toolkit-docs-generator/data/toolkits/boxapi.json @@ -10979,7 +10979,7 @@ ], "documentationChunks": [], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.096Z", diff --git a/toolkit-docs-generator/data/toolkits/calendlyapi.json b/toolkit-docs-generator/data/toolkits/calendlyapi.json index ea785bba7..59efbb941 100644 --- a/toolkit-docs-generator/data/toolkits/calendlyapi.json +++ b/toolkit-docs-generator/data/toolkits/calendlyapi.json @@ -3413,7 +3413,7 @@ ], "documentationChunks": [], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.043Z", diff --git a/toolkit-docs-generator/data/toolkits/clickupapi.json b/toolkit-docs-generator/data/toolkits/clickupapi.json index 74c35f9e6..cf0262595 100644 --- a/toolkit-docs-generator/data/toolkits/clickupapi.json +++ b/toolkit-docs-generator/data/toolkits/clickupapi.json @@ -10595,7 +10595,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.070Z", diff --git a/toolkit-docs-generator/data/toolkits/cursoragentsapi.json b/toolkit-docs-generator/data/toolkits/cursoragentsapi.json index 2f10ad93d..6051b5202 100644 --- a/toolkit-docs-generator/data/toolkits/cursoragentsapi.json +++ b/toolkit-docs-generator/data/toolkits/cursoragentsapi.json @@ -273,7 +273,7 @@ ], "documentationChunks": [], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-01-26T17:27:54.003Z", diff --git a/toolkit-docs-generator/data/toolkits/customerioapi.json b/toolkit-docs-generator/data/toolkits/customerioapi.json index 7b69f8563..1ae6ae2d4 100644 --- a/toolkit-docs-generator/data/toolkits/customerioapi.json +++ b/toolkit-docs-generator/data/toolkits/customerioapi.json @@ -7660,7 +7660,7 @@ ], "documentationChunks": [], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.061Z", diff --git a/toolkit-docs-generator/data/toolkits/customeriopipelinesapi.json b/toolkit-docs-generator/data/toolkits/customeriopipelinesapi.json index b6dbda795..082fc7cee 100644 --- a/toolkit-docs-generator/data/toolkits/customeriopipelinesapi.json +++ b/toolkit-docs-generator/data/toolkits/customeriopipelinesapi.json @@ -529,7 +529,7 @@ ], "documentationChunks": [], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.051Z", diff --git a/toolkit-docs-generator/data/toolkits/customeriotrackapi.json b/toolkit-docs-generator/data/toolkits/customeriotrackapi.json index 5bf1ed77e..44620b19c 100644 --- a/toolkit-docs-generator/data/toolkits/customeriotrackapi.json +++ b/toolkit-docs-generator/data/toolkits/customeriotrackapi.json @@ -1142,7 +1142,7 @@ ], "documentationChunks": [], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.071Z", diff --git a/toolkit-docs-generator/data/toolkits/datadogapi.json b/toolkit-docs-generator/data/toolkits/datadogapi.json index 2843b8c55..60c07da9a 100644 --- a/toolkit-docs-generator/data/toolkits/datadogapi.json +++ b/toolkit-docs-generator/data/toolkits/datadogapi.json @@ -44866,7 +44866,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.286Z", diff --git a/toolkit-docs-generator/data/toolkits/exaapi.json b/toolkit-docs-generator/data/toolkits/exaapi.json index c0f9c4e2d..68e6ce53d 100644 --- a/toolkit-docs-generator/data/toolkits/exaapi.json +++ b/toolkit-docs-generator/data/toolkits/exaapi.json @@ -2400,7 +2400,7 @@ ], "documentationChunks": [], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.097Z", diff --git a/toolkit-docs-generator/data/toolkits/figmaapi.json b/toolkit-docs-generator/data/toolkits/figmaapi.json index 0340c8222..a4a65c68d 100644 --- a/toolkit-docs-generator/data/toolkits/figmaapi.json +++ b/toolkit-docs-generator/data/toolkits/figmaapi.json @@ -2913,7 +2913,7 @@ ], "documentationChunks": [], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.123Z", diff --git a/toolkit-docs-generator/data/toolkits/freshserviceapi.json b/toolkit-docs-generator/data/toolkits/freshserviceapi.json index 62c09061a..1cf9880f8 100644 --- a/toolkit-docs-generator/data/toolkits/freshserviceapi.json +++ b/toolkit-docs-generator/data/toolkits/freshserviceapi.json @@ -14928,7 +14928,7 @@ ], "documentationChunks": [], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.168Z", diff --git a/toolkit-docs-generator/data/toolkits/github.json b/toolkit-docs-generator/data/toolkits/github.json index f41417a3e..fcf38735b 100644 --- a/toolkit-docs-generator/data/toolkits/github.json +++ b/toolkit-docs-generator/data/toolkits/github.json @@ -5815,10 +5815,11 @@ } ], "customImports": [ - "import { Callout, Tabs } from \"nextra/components\";" + "import { Callout } from \"@/components/mdx/callout\";", + "import { Tabs } from \"@/components/mdx/tabs\";" ], "subPages": [], - "curationSourceHash": "c650cdab79c81731cd0ee555fe582122e58496b6071761e71e79fdb19a739d69", + "curationSourceHash": "664fcc9e2237721923091c7504a53aae1a803ff2be8fd41704c8deb107b3c470", "generatedAt": "2026-10-07T21:25:10.779Z", "summary": "## GitHub Toolkit\n\nArcade's GitHub toolkit connects LLMs to the GitHub API, enabling agents to manage repositories, issues, pull requests, projects, code search, and user activity on behalf of authenticated users.\n\n## Capabilities\n\n- **Repository & file management:** Create branches, read/write/update files with explicit create-vs-overwrite modes, list org repos, fetch repo details, and track repository activity history.\n- **Pull request lifecycle:** Open, update, merge, and inspect PRs; check merge readiness; list commits; assign users and manage reviewers with fuzzy matching; submit, reply to, and resolve review comments and threads.\n- **Issues & labels:** Create, update, and fetch issues (including project assignment); add comments; add/remove labels with typo-tolerant fuzzy matching; list repository labels.\n- **GitHub Projects (V2):** List projects across org/user scopes, enumerate project fields and items, and search for specific project items.\n- **Code & user search:** Full-text code search with inline match context, fuzzy repo search, and authenticated-user activity summaries (open items, recent PRs/reviews/commits, review workload).\n- **Social & metadata:** Star/un-star repos, count and list stargazers, and retrieve the authenticated user's profile, organizations, and teams.\n\n## OAuth\n\nThis toolkit uses OAuth 2.0 via the **GitHub** provider. See the [Arcade GitHub auth provider docs](https://docs.arcade.dev/en/references/auth-providers/github) for configuration details.\n\n## Secrets\n\n- **`GITHUB_SERVER_URL`** — The base URL of the GitHub API server. This is relevant when targeting a **GitHub Enterprise Server (GHES)** instance rather than the public `https://github.com`. Set this to your enterprise instance's API root (e.g., `https://github.your-company.com`). You obtain this value from your GitHub Enterprise Server administrator or your organization's internal documentation — it is not a credential but a URL override. If you are using public GitHub, this secret is not required. For how to create and manage secrets in Arcade, see the [Arcade secrets docs](https://docs.arcade.dev/en/guides/create-tools/tool-basics/create-tool-secrets). You can also manage secrets at [https://api.arcade.dev/dashboard/auth/secrets](https://api.arcade.dev/dashboard/auth/secrets)." } \ No newline at end of file diff --git a/toolkit-docs-generator/data/toolkits/githubapi.json b/toolkit-docs-generator/data/toolkits/githubapi.json index 749493750..fa798d650 100644 --- a/toolkit-docs-generator/data/toolkits/githubapi.json +++ b/toolkit-docs-generator/data/toolkits/githubapi.json @@ -65939,7 +65939,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.397Z", diff --git a/toolkit-docs-generator/data/toolkits/gmail.json b/toolkit-docs-generator/data/toolkits/gmail.json index cc9ea5a9d..fd2205990 100644 --- a/toolkit-docs-generator/data/toolkits/gmail.json +++ b/toolkit-docs-generator/data/toolkits/gmail.json @@ -2991,10 +2991,10 @@ } ], "customImports": [ - "import ScopePicker from \"@/app/_components/scope-picker\";" + "import ScopePicker from \"@/components/scope-picker\";" ], "subPages": [], - "curationSourceHash": "398ba8f3cd2e22b631d4b91d1598db5c4947f763824bc05c5bc5f38ca7b83e3f", + "curationSourceHash": "ca7fe11472ff121114b9ad53cb1b98f18058b5ea4ff8a792014d20faf8b0170c", "generatedAt": "2026-10-07T21:25:14.046Z", "summary": "The Gmail toolkit lets agents interact with Gmail via the Arcade platform, enabling reading, composing, organizing, and triaging email at scale.\n\n## Capabilities\n\n- **Reading & searching** — fetch individual messages or threads by ID, list emails/threads/drafts with optional body inclusion, search by raw Gmail query (with lightweight, full, or count-only modes), and list attachment metadata without loading file bytes.\n- **Composing & sending** — send new emails, reply to or forward messages, and attach local files via `file://` URIs; file bytes are substituted client-side and never transit the conversation.\n- **Draft management** — create, update (with content-type-aware body replacement and reply-quote preservation), send, and delete drafts; reply drafts are also supported.\n- **Organization & labeling** — create, rename, and delete user labels; add/remove labels on individual messages (up to 50 per call) or entire threads; list all mailbox labels.\n- **Triage & inbox control** — mark read/unread, star/unstar, archive, trash/untrash, restore, flag importance, report spam, or unsubscribe from marketing mail; bulk triage processes up to 50 messages per call with idempotent per-message state transitions.\n- **Account info** — retrieve the authenticated user's profile, email address, and Gmail account statistics.\n\n## OAuth\n\nThis toolkit uses OAuth 2.0 via Google. See the [Arcade Google auth provider docs](https://docs.arcade.dev/en/references/auth-providers/google) for setup details." } \ No newline at end of file diff --git a/toolkit-docs-generator/data/toolkits/googlecalendar.json b/toolkit-docs-generator/data/toolkits/googlecalendar.json index 3f6e68b05..b4488d78c 100644 --- a/toolkit-docs-generator/data/toolkits/googlecalendar.json +++ b/toolkit-docs-generator/data/toolkits/googlecalendar.json @@ -1135,10 +1135,10 @@ } ], "customImports": [ - "import ScopePicker from \"@/app/_components/scope-picker\";" + "import ScopePicker from \"@/components/scope-picker\";" ], "subPages": [], - "curationSourceHash": "cd977f2e66058d686da277f9a3c43e903d6caba18a9124ec18a51996ad31882d", + "curationSourceHash": "834fd74f529c86cf6a9048fe2c9c7f4faadef22e689ada61c0d7c1fb0e0dad67", "generatedAt": "2026-10-07T21:25:15.717Z", "summary": "## Google Calendar Toolkit\n\nThe Google Calendar toolkit connects Arcade to Google Calendar, enabling LLMs to read, create, update, and manage calendar events and availability on behalf of authenticated users.\n\n## Capabilities\n\n- **Event CRUD**: Create events (including recurring series via `recurrence`), retrieve a single event by ID (including cancelled/deleted occurrences and series recurrence rules), list events within a datetime window with precise boundary semantics, update any subset of event fields or move an event to another calendar, and delete events.\n- **Availability & scheduling**: Find time slots when all specified participants are free within a given date range and time boundaries.\n- **RSVP / response management**: Accept, decline, or tentatively accept meeting invitations on the authenticated user's behalf.\n- **Calendar discovery**: List all calendars accessible to the authenticated user.\n- **User identity**: Retrieve the authenticated user's profile, email, and Google Calendar access permissions.\n\n## OAuth\n\nThis toolkit uses OAuth 2.0 via the **Google** provider. See the [Arcade Google auth provider docs](https://docs.arcade.dev/en/references/auth-providers/google) for setup and configuration details." } \ No newline at end of file diff --git a/toolkit-docs-generator/data/toolkits/googlecontacts.json b/toolkit-docs-generator/data/toolkits/googlecontacts.json index 1fc4a3164..02f04df0e 100644 --- a/toolkit-docs-generator/data/toolkits/googlecontacts.json +++ b/toolkit-docs-generator/data/toolkits/googlecontacts.json @@ -474,10 +474,10 @@ } ], "customImports": [ - "import ScopePicker from \"@/app/_components/scope-picker\";" + "import ScopePicker from \"@/components/scope-picker\";" ], "subPages": [], - "curationSourceHash": "0e75899caacecfd43341e92905a98a366288b010b1c7fc01eae96dcbecb40071", + "curationSourceHash": "d00b1b580f072092120626958b8c3cc909c0c80019d7500f5cd020c32fd2e0f4", "generatedAt": "2026-10-07T21:25:11.909Z", "summary": "The Google Contacts toolkit lets LLMs create and search contact records in a user's personal Google Contacts and, for Workspace users, query the organizational directory.\n\n## Capabilities\n\n- **Contact creation** — create new personal contact records with any combination of given name, family name, email, and phone number.\n- **Multi-field personal contact search** — find contacts by name, email address, or phone number across the authenticated user's personal contacts.\n- **Workspace directory search** — resolve colleagues by name or email against the Google Workspace org directory, with paginated results (pass `next_page_token` with identical query and limit to continue; Google rejects mismatched continuation calls).\n- **Identity & environment inspection** — retrieve the authenticated user's profile (name, email, photo, Contacts permissions) via `WhoAmI`, useful for debugging auth state or personalizing responses.\n\n## OAuth\n\nThis toolkit uses OAuth 2.0 via the **Google** provider. See the [Arcade Google auth provider docs](https://docs.arcade.dev/en/references/auth-providers/google) for configuration details." } \ No newline at end of file diff --git a/toolkit-docs-generator/data/toolkits/googledocs.json b/toolkit-docs-generator/data/toolkits/googledocs.json index 2d930a4db..2e0284fe1 100644 --- a/toolkit-docs-generator/data/toolkits/googledocs.json +++ b/toolkit-docs-generator/data/toolkits/googledocs.json @@ -1315,10 +1315,10 @@ } ], "customImports": [ - "import ScopePicker from \"@/app/_components/scope-picker\";" + "import ScopePicker from \"@/components/scope-picker\";" ], "subPages": [], - "curationSourceHash": "33910354b796df23626997fca5e7d53bbf1479131ac0d258bc282833e12de451", + "curationSourceHash": "ef4133d9fd94ccf463480c1158d02d68488e36e2f99af57d83c94c384572a72a", "generatedAt": "2026-10-07T21:25:11.945Z", "summary": "## Google Docs Toolkit\n\nThe Google Docs toolkit connects Arcade to the Google Docs and Drive APIs, enabling LLMs to create, read, edit, search, and comment on documents on behalf of a connected Google account.\n\n## Capabilities\n\n- **Document creation**: Create blank documents or documents pre-populated with plain text or Markdown (headings, bold, italic, lists) in the connected account's Drive root.\n- **Reading and metadata**: Retrieve documents in raw API form, as compact DocMD (block IDs, character indices, styles), or as metadata only (title, ID, URL, tab structure, character counts).\n- **Editing and appending**: Apply structured `batchUpdate` requests to any accessible document using DocMD indices, or append text to the end of a specific tab.\n- **Comments**: Post whole-document comments as the connected account, and list a document's comments with replies, authors, quoted passages, and resolved status — paged for large threads.\n- **Search**: Search the accessible document corpus by query, returning metadata only or metadata plus body content; trash is excluded automatically.\n- **Access management and identity**: Generate a Google Drive file picker URL so users can grant per-file access to the app, and resolve the connected account's profile (name, email, avatar) via `WhoAmI`.\n\n## OAuth\n\nThis toolkit uses OAuth 2.0 with **Google** as the provider. See the [Arcade Google auth provider docs](https://docs.arcade.dev/en/references/auth-providers/google) for setup details.\n\n## Secrets\n\n- **`ENABLE_GOOGLE_DRIVE_INLINE_PICKER_URL`** — Controls whether the `GenerateGoogleFilePickerUrl` tool can surface a Google Drive file picker URL to end users. This is an Arcade-side configuration secret rather than a credential issued by Google; its value governs feature enablement for the inline picker flow. Set it through the [Arcade secrets dashboard](https://api.arcade.dev/dashboard/auth/secrets) or via the Arcade config. See [Arcade tool secrets docs](https://docs.arcade.dev/en/guides/create-tools/tool-basics/create-tool-secrets) for full configuration instructions." } \ No newline at end of file diff --git a/toolkit-docs-generator/data/toolkits/googledrive.json b/toolkit-docs-generator/data/toolkits/googledrive.json index 4653fc205..edec7f813 100644 --- a/toolkit-docs-generator/data/toolkits/googledrive.json +++ b/toolkit-docs-generator/data/toolkits/googledrive.json @@ -1772,10 +1772,10 @@ ], "documentationChunks": [], "customImports": [ - "import ScopePicker from \"@/app/_components/scope-picker\";" + "import ScopePicker from \"@/components/scope-picker\";" ], "subPages": [], - "curationSourceHash": "dd8b363d0c5b211c25a2d7aca421340a81cbdc1eebee6d6743d73838563477a4", + "curationSourceHash": "31f3afa4ed7a96136459edc164c030b1f542448a05c054a41eb4a11c574f75e6", "generatedAt": "2026-10-07T21:25:12.467Z", "summary": "The Google Drive toolkit connects LLMs to Google Drive via Arcade, enabling agents to read, write, organize, share, and audit Drive files and folders on behalf of authenticated users.\n\n## Capabilities\n\n- **File discovery & access checks** — search files, retrieve the full folder tree, check batch accessibility before reading, and look up the authenticated user's profile and storage environment.\n- **File I/O** — download files directly (small files) or in byte-range chunks (large files), upload files from a URL (up to 25 MB, non-Workspace formats), and generate a Google file picker URL to request per-file access grants.\n- **Organization** — create folders, move files within a drive, rename files or folders, and support both My Drive and shared drives.\n- **Permissions & sharing** — share files with individuals (with role upsert), list all collaborators and link-sharing state, change general/link-sharing access, revoke access for specific people or groups, and bulk-remove all collaborators with fine-grained exceptions.\n\n## OAuth\n\nThis toolkit uses **OAuth 2.0** via the **Google** provider. See the [Arcade Google auth provider docs](https://docs.arcade.dev/en/references/auth-providers/google) for setup instructions.\n\n## Secrets\n\n`ENABLE_GOOGLE_DRIVE_INLINE_PICKER_URL` — controls whether `GoogleDrive.CheckFileAccess` and `GoogleDrive.GenerateGoogleFilePickerUrl` return an inline Google Drive picker URL for granting per-file access. When this secret is set (to a truthy/enabled value), the toolkit will embed a picker URL in responses so users can grant access without leaving the agent flow. To enable this feature, obtain or configure the picker URL endpoint from your Arcade deployment settings (this is an Arcade-side capability, not a raw Google credential). Configure secrets in the [Arcade dashboard](https://api.arcade.dev/dashboard/auth/secrets) and see the [Arcade secrets docs](https://docs.arcade.dev/en/guides/create-tools/tool-basics/create-tool-secrets) for general guidance." } \ No newline at end of file diff --git a/toolkit-docs-generator/data/toolkits/googlesheets.json b/toolkit-docs-generator/data/toolkits/googlesheets.json index 814df6fe8..b84d9d8e4 100644 --- a/toolkit-docs-generator/data/toolkits/googlesheets.json +++ b/toolkit-docs-generator/data/toolkits/googlesheets.json @@ -1951,10 +1951,10 @@ ], "documentationChunks": [], "customImports": [ - "import ScopePicker from \"@/app/_components/scope-picker\";" + "import ScopePicker from \"@/components/scope-picker\";" ], "subPages": [], - "curationSourceHash": "dd8b363d0c5b211c25a2d7aca421340a81cbdc1eebee6d6743d73838563477a4", + "curationSourceHash": "31f3afa4ed7a96136459edc164c030b1f542448a05c054a41eb4a11c574f75e6", "generatedAt": "2026-10-07T21:25:23.561Z", "summary": "## Google Sheets Toolkit\n\nArcade's Google Sheets toolkit gives LLMs structured, authenticated access to Google Sheets and Drive, enabling agents to read, write, inspect, search, comment on, and audit spreadsheets via tool calls.\n\n## Capabilities\n\n- **Read & inspect:** Fetch spreadsheet structure (tabs, charts, merges, conditional formats, protected ranges) or read cell ranges as grids with optional annotations and markdown/CSV/TSV export; smart pre-flight access checks batch-verify reachability of multiple spreadsheets before reading.\n- **Write & create:** Create new spreadsheets or batch-edit existing ones using the full Sheets `requests[]` API (cell updates, sheet management, sorting, conditional formatting, auto-resize, and more).\n- **Search & discover:** Search Google Drive for spreadsheets by title/content, returning metadata (title, ID, URL); identify the connected Google account via the People API.\n- **Comments & collaboration:** List comment threads and replies, create file-level comments, edit comment bodies, resolve/reopen threads, add replies, and delete comments.\n- **Data quality:** Deterministically scan one or all tabs for bad/weird cells (no LLM judgment), returning flagged cells with coordinates, severity, and rule — ready to pipe into a format/annotate workflow.\n- **Edit history:** Report revision history (who edited, when) from Drive revisions, with summary or paginated list modes.\n\n## OAuth\n\nThis toolkit uses OAuth 2.0 via Google. See the [Arcade Google auth provider docs](https://docs.arcade.dev/en/references/auth-providers/google) for provider configuration details.\n\n## Secrets\n\n- **`ENABLE_GOOGLE_DRIVE_INLINE_PICKER_URL`** — Controls whether the toolkit generates an inline Google Drive file-picker URL as part of access-check responses (`CheckSpreadsheetAccess`, `GenerateGoogleFilePickerUrl`). When enabled, the picker URL is embedded directly in the tool response so the user can grant spreadsheet access without a separate redirect step. This is an Arcade-side feature flag, not a credential issued by Google; obtain or configure it through the [Arcade secrets dashboard](https://api.arcade.dev/dashboard/auth/secrets). Refer to the [Arcade secrets documentation](https://docs.arcade.dev/en/guides/create-tools/tool-basics/create-tool-secrets) for setup instructions." } \ No newline at end of file diff --git a/toolkit-docs-generator/data/toolkits/googleslides.json b/toolkit-docs-generator/data/toolkits/googleslides.json index dc487ea2c..8fe2bc082 100644 --- a/toolkit-docs-generator/data/toolkits/googleslides.json +++ b/toolkit-docs-generator/data/toolkits/googleslides.json @@ -1175,10 +1175,10 @@ } ], "customImports": [ - "import ScopePicker from \"@/app/_components/scope-picker\";" + "import ScopePicker from \"@/components/scope-picker\";" ], "subPages": [], - "curationSourceHash": "b3d8f14f84f11cfab50aa2251f31e3213b2612eb819c1bac2b8979c2fb898f20", + "curationSourceHash": "04b922af39e712b507fdb2c7be4746718a35c48e98c61f4af7fb55705bf62ea8", "generatedAt": "2026-10-07T21:25:24.455Z", "summary": "## Google Slides Toolkit\n\nThe Google Slides toolkit connects Arcade to Google Slides (and Drive) so LLMs can create, read, edit, comment on, and search presentations on behalf of a user.\n\n## Capabilities\n\n- **Deck creation & editing:** Create presentations from scratch or derive branded decks from an existing template; apply atomic batches of edits (add/delete/restyle slides and elements, insert/replace text, reorder slides, duplicate objects, set speaker notes) in a single call.\n- **Read & snapshot:** Retrieve a full addressable snapshot of any deck — object IDs, placeholder types, text, speaker notes, layout/master inventory — to feed the read-edit loop or answer questions about content.\n- **Visual preview:** Render any slide to PNG (content URL or base64 data URI) at configurable sizes to verify layout, overflow, and branding before sharing.\n- **Comments & lifecycle:** Add top-level comments or thread replies, list paginated comments newest-first, and resolve or reopen comment threads (with attribution).\n- **Drive search & file access:** Full-text search across the user's Drive for presentations; generate a Google Drive inline file picker URL to request per-file access when a presentation is not found or access is denied.\n- **Account identity:** Retrieve the connected Google account's profile (name, email, picture).\n\n## OAuth\n\nThis toolkit uses **OAuth 2.0** via Google. See the [Arcade Google auth provider docs](https://docs.arcade.dev/en/references/auth-providers/google) for setup details.\n\n## Secrets\n\n- **`ENABLE_GOOGLE_DRIVE_INLINE_PICKER_URL`** — An API key that enables the `GenerateGoogleFilePickerUrl` tool, which surfaces Google's first-party Drive file picker so users can grant per-file access. To obtain this key, you must configure a Google API key in the [Google Cloud Console](https://console.cloud.google.com/apis/credentials) with the **Google Picker API** enabled for your project. Navigate to **APIs & Services → Credentials → Create Credentials → API key**, enable the **Google Picker API** under **APIs & Services → Library**, and optionally restrict the key to that API and your allowed HTTP referrers. The resulting key string is the secret value.\n\nSee the [Arcade secrets docs](https://docs.arcade.dev/en/guides/create-tools/tool-basics/create-tool-secrets) for how to store secrets, or manage them directly at [https://api.arcade.dev/dashboard/auth/secrets](https://api.arcade.dev/dashboard/auth/secrets)." } \ No newline at end of file diff --git a/toolkit-docs-generator/data/toolkits/hubspotautomationapi.json b/toolkit-docs-generator/data/toolkits/hubspotautomationapi.json index a3dbd902f..98d85b0f0 100644 --- a/toolkit-docs-generator/data/toolkits/hubspotautomationapi.json +++ b/toolkit-docs-generator/data/toolkits/hubspotautomationapi.json @@ -565,7 +565,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.139Z", diff --git a/toolkit-docs-generator/data/toolkits/hubspotcmsapi.json b/toolkit-docs-generator/data/toolkits/hubspotcmsapi.json index 7026e2a28..3cf905db3 100644 --- a/toolkit-docs-generator/data/toolkits/hubspotcmsapi.json +++ b/toolkit-docs-generator/data/toolkits/hubspotcmsapi.json @@ -12790,7 +12790,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.179Z", diff --git a/toolkit-docs-generator/data/toolkits/hubspotconversationsapi.json b/toolkit-docs-generator/data/toolkits/hubspotconversationsapi.json index 28cc029b9..44f1175ad 100644 --- a/toolkit-docs-generator/data/toolkits/hubspotconversationsapi.json +++ b/toolkit-docs-generator/data/toolkits/hubspotconversationsapi.json @@ -1348,7 +1348,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.145Z", diff --git a/toolkit-docs-generator/data/toolkits/hubspotcrmapi.json b/toolkit-docs-generator/data/toolkits/hubspotcrmapi.json index c61d994a0..fe74013b0 100644 --- a/toolkit-docs-generator/data/toolkits/hubspotcrmapi.json +++ b/toolkit-docs-generator/data/toolkits/hubspotcrmapi.json @@ -37311,7 +37311,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.259Z", diff --git a/toolkit-docs-generator/data/toolkits/hubspoteventsapi.json b/toolkit-docs-generator/data/toolkits/hubspoteventsapi.json index b1e4edd96..abe8aa564 100644 --- a/toolkit-docs-generator/data/toolkits/hubspoteventsapi.json +++ b/toolkit-docs-generator/data/toolkits/hubspoteventsapi.json @@ -367,7 +367,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.151Z", diff --git a/toolkit-docs-generator/data/toolkits/hubspotmarketingapi.json b/toolkit-docs-generator/data/toolkits/hubspotmarketingapi.json index 1ba6f44ef..6c993d37d 100644 --- a/toolkit-docs-generator/data/toolkits/hubspotmarketingapi.json +++ b/toolkit-docs-generator/data/toolkits/hubspotmarketingapi.json @@ -5852,7 +5852,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.179Z", diff --git a/toolkit-docs-generator/data/toolkits/hubspotmeetingsapi.json b/toolkit-docs-generator/data/toolkits/hubspotmeetingsapi.json index 05b0fbfd4..2660c78a9 100644 --- a/toolkit-docs-generator/data/toolkits/hubspotmeetingsapi.json +++ b/toolkit-docs-generator/data/toolkits/hubspotmeetingsapi.json @@ -325,7 +325,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.164Z", diff --git a/toolkit-docs-generator/data/toolkits/hubspotusersapi.json b/toolkit-docs-generator/data/toolkits/hubspotusersapi.json index 9aef3c993..f02756c53 100644 --- a/toolkit-docs-generator/data/toolkits/hubspotusersapi.json +++ b/toolkit-docs-generator/data/toolkits/hubspotusersapi.json @@ -516,7 +516,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.199Z", diff --git a/toolkit-docs-generator/data/toolkits/intercomapi.json b/toolkit-docs-generator/data/toolkits/intercomapi.json index a82d4109e..5e4cee941 100644 --- a/toolkit-docs-generator/data/toolkits/intercomapi.json +++ b/toolkit-docs-generator/data/toolkits/intercomapi.json @@ -6367,7 +6367,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.226Z", diff --git a/toolkit-docs-generator/data/toolkits/jira.json b/toolkit-docs-generator/data/toolkits/jira.json index a1ae99907..d0e85f18e 100644 --- a/toolkit-docs-generator/data/toolkits/jira.json +++ b/toolkit-docs-generator/data/toolkits/jira.json @@ -4573,11 +4573,11 @@ "subPages": [ { "type": "environment-variables", - "content": "import { Callout } from \"nextra/components\";\n\n# Jira Environment Variables\n\n### `JIRA_MAX_CONCURRENT_REQUESTS`\n\nArcade uses asynchronous calls to request Jira API endpoints. In some tools, multiple concurrent HTTP requests may be made to speed up execution. This environment variable controls the maximum number of concurrent requests to Jira API in any tool execution.\n\nThe value must be a numeric string with an integer greater than or equal to 1.\n\n**Default:** `3`\n\n\n### `JIRA_API_REQUEST_TIMEOUT`\n\nControls the maximum number of seconds to wait for a response from the Jira API. This is also applied, in some cases, as a global max timeout for multiple requests that are made in a single tool execution. For instance, when a tool needs to paginate results from a given endpoint, this timeout may apply to the entire pagination process in total, not only to the individual requests.\n\nThe value must be a numeric string with an integer greater than or equal to 1.\n\n**Default:** `30`\n\n\n### `JIRA_CACHE_MAX_ITEMS`\n\n<Callout type=\"info\">\n The caching strategy does not involve caching Jira API responses that go into tool output, but only internal values.\n</Callout>\n\nThe Arcade Jira MCP Server will cache some values that are repeatedly used in tool execution to enable better performance. This environment variable controls the maximum number of items to hold in each cache.\n\nThe value must be a numeric string with an integer greater than or equal to 1.\n\n**Default:** `5000`\n", + "content": "import { Callout } from \"@/components/mdx/callout\";\n\n# Jira Environment Variables\n\n### `JIRA_MAX_CONCURRENT_REQUESTS`\n\nArcade uses asynchronous calls to request Jira API endpoints. In some tools, multiple concurrent HTTP requests may be made to speed up execution. This environment variable controls the maximum number of concurrent requests to Jira API in any tool execution.\n\nThe value must be a numeric string with an integer greater than or equal to 1.\n\n**Default:** `3`\n\n\n### `JIRA_API_REQUEST_TIMEOUT`\n\nControls the maximum number of seconds to wait for a response from the Jira API. This is also applied, in some cases, as a global max timeout for multiple requests that are made in a single tool execution. For instance, when a tool needs to paginate results from a given endpoint, this timeout may apply to the entire pagination process in total, not only to the individual requests.\n\nThe value must be a numeric string with an integer greater than or equal to 1.\n\n**Default:** `30`\n\n\n### `JIRA_CACHE_MAX_ITEMS`\n\n<Callout type=\"info\">\n The caching strategy does not involve caching Jira API responses that go into tool output, but only internal values.\n</Callout>\n\nThe Arcade Jira MCP Server will cache some values that are repeatedly used in tool execution to enable better performance. This environment variable controls the maximum number of items to hold in each cache.\n\nThe value must be a numeric string with an integer greater than or equal to 1.\n\n**Default:** `5000`\n", "relativePath": "environment-variables/page.mdx" } ], - "curationSourceHash": "8f665e06935e6bdc3eb98b876a1dcfce71b4004d4758ddf8c5171c6161aca02e", + "curationSourceHash": "84e4f02a545f510d86a19a52f763c3566773c9f4889a534b856eb22660a38ccd", "generatedAt": "2026-10-07T21:25:34.632Z", "summary": "The Jira toolkit integrates Arcade with Atlassian Jira, enabling LLMs to manage issues, sprints, boards, users, attachments, and project metadata across Jira Cloud instances.\n\n## Capabilities\n\n- **Issue lifecycle management** — Create, update, transition, search (parameterized or JQL), comment on, label, and attach files to issues; move issues between sprints and backlogs. Descriptions accept Markdown, and issues can carry components, fix versions, and custom fields.\n- **Sprint & board operations** — List boards and their sprints (with date-range filtering), retrieve backlog and sprint issues, add/move issues to sprints, and access backlog GUI URLs.\n- **Project & metadata discovery** — Browse projects, issue types, priority schemes, labels, and available transitions; resolve names/keys/emails to IDs automatically without requiring pre-lookup calls.\n- **User & identity management** — Look up users by ID, name, or email; list all users; retrieve the authenticated user's profile and available Atlassian Cloud instances.\n- **Attachment handling** — List attachment metadata, download attachment contents, and upload files (string or base64) to issues.\n\n## OAuth\n\nAuthentication uses OAuth 2.0 via the **Atlassian** provider. See the [Atlassian auth provider docs](https://docs.arcade.dev/en/references/auth-providers/atlassian) for setup details, required scopes, and configuration steps." } \ No newline at end of file diff --git a/toolkit-docs-generator/data/toolkits/lumaapi.json b/toolkit-docs-generator/data/toolkits/lumaapi.json index e8f646661..7ee4d89a5 100644 --- a/toolkit-docs-generator/data/toolkits/lumaapi.json +++ b/toolkit-docs-generator/data/toolkits/lumaapi.json @@ -2512,7 +2512,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.214Z", diff --git a/toolkit-docs-generator/data/toolkits/mailchimpmarketingapi.json b/toolkit-docs-generator/data/toolkits/mailchimpmarketingapi.json index f8ef4588f..a1607a87e 100644 --- a/toolkit-docs-generator/data/toolkits/mailchimpmarketingapi.json +++ b/toolkit-docs-generator/data/toolkits/mailchimpmarketingapi.json @@ -22288,7 +22288,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.324Z", diff --git a/toolkit-docs-generator/data/toolkits/miroapi.json b/toolkit-docs-generator/data/toolkits/miroapi.json index a0edfd917..d402a8d2b 100644 --- a/toolkit-docs-generator/data/toolkits/miroapi.json +++ b/toolkit-docs-generator/data/toolkits/miroapi.json @@ -10093,7 +10093,7 @@ ], "documentationChunks": [], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.316Z", diff --git a/toolkit-docs-generator/data/toolkits/pagerdutyapi.json b/toolkit-docs-generator/data/toolkits/pagerdutyapi.json index 06c1a4c4e..d98687c2e 100644 --- a/toolkit-docs-generator/data/toolkits/pagerdutyapi.json +++ b/toolkit-docs-generator/data/toolkits/pagerdutyapi.json @@ -24616,7 +24616,7 @@ ], "documentationChunks": [], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.437Z", diff --git a/toolkit-docs-generator/data/toolkits/posthogapi.json b/toolkit-docs-generator/data/toolkits/posthogapi.json index 587f352c4..1bbfab715 100644 --- a/toolkit-docs-generator/data/toolkits/posthogapi.json +++ b/toolkit-docs-generator/data/toolkits/posthogapi.json @@ -60803,7 +60803,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.590Z", diff --git a/toolkit-docs-generator/data/toolkits/slackapi.json b/toolkit-docs-generator/data/toolkits/slackapi.json index 18bb7e9be..95d1aa6f4 100644 --- a/toolkit-docs-generator/data/toolkits/slackapi.json +++ b/toolkit-docs-generator/data/toolkits/slackapi.json @@ -5969,7 +5969,7 @@ ], "documentationChunks": [], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.373Z", diff --git a/toolkit-docs-generator/data/toolkits/squareupapi.json b/toolkit-docs-generator/data/toolkits/squareupapi.json index 91e529d98..412b33843 100644 --- a/toolkit-docs-generator/data/toolkits/squareupapi.json +++ b/toolkit-docs-generator/data/toolkits/squareupapi.json @@ -19364,7 +19364,7 @@ ], "documentationChunks": [], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.493Z", diff --git a/toolkit-docs-generator/data/toolkits/stripeapi.json b/toolkit-docs-generator/data/toolkits/stripeapi.json index 062eec4eb..eedb86484 100644 --- a/toolkit-docs-generator/data/toolkits/stripeapi.json +++ b/toolkit-docs-generator/data/toolkits/stripeapi.json @@ -16115,7 +16115,7 @@ ], "documentationChunks": [], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.476Z", diff --git a/toolkit-docs-generator/data/toolkits/ticktickapi.json b/toolkit-docs-generator/data/toolkits/ticktickapi.json index 773b8b3cd..d3cf7fb70 100644 --- a/toolkit-docs-generator/data/toolkits/ticktickapi.json +++ b/toolkit-docs-generator/data/toolkits/ticktickapi.json @@ -691,7 +691,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.459Z", diff --git a/toolkit-docs-generator/data/toolkits/trelloapi.json b/toolkit-docs-generator/data/toolkits/trelloapi.json index 1e92773a7..4360285ee 100644 --- a/toolkit-docs-generator/data/toolkits/trelloapi.json +++ b/toolkit-docs-generator/data/toolkits/trelloapi.json @@ -18901,7 +18901,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.529Z", diff --git a/toolkit-docs-generator/data/toolkits/vercelapi.json b/toolkit-docs-generator/data/toolkits/vercelapi.json index 4abcb0d79..df1c2711f 100644 --- a/toolkit-docs-generator/data/toolkits/vercelapi.json +++ b/toolkit-docs-generator/data/toolkits/vercelapi.json @@ -17085,7 +17085,7 @@ ], "documentationChunks": [], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.512Z", diff --git a/toolkit-docs-generator/data/toolkits/weaviateapi.json b/toolkit-docs-generator/data/toolkits/weaviateapi.json index fbd538b25..46e7ef894 100644 --- a/toolkit-docs-generator/data/toolkits/weaviateapi.json +++ b/toolkit-docs-generator/data/toolkits/weaviateapi.json @@ -5588,7 +5588,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.530Z", diff --git a/toolkit-docs-generator/data/toolkits/xeroapi.json b/toolkit-docs-generator/data/toolkits/xeroapi.json index ec348144b..d8d506004 100644 --- a/toolkit-docs-generator/data/toolkits/xeroapi.json +++ b/toolkit-docs-generator/data/toolkits/xeroapi.json @@ -11180,7 +11180,7 @@ ], "documentationChunks": [], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-07-24T11:39:02.151Z", diff --git a/toolkit-docs-generator/data/toolkits/zohobooksapi.json b/toolkit-docs-generator/data/toolkits/zohobooksapi.json index 1705cc7a4..12b1efb47 100644 --- a/toolkit-docs-generator/data/toolkits/zohobooksapi.json +++ b/toolkit-docs-generator/data/toolkits/zohobooksapi.json @@ -44925,7 +44925,7 @@ } ], "customImports": [ - "import StarterToolInfo from \"@/app/_components/starter-tool-info\";" + "import StarterToolInfo from \"@/components/starter-tool-info\";" ], "subPages": [], "generatedAt": "2026-04-10T11:26:08.641Z", diff --git a/toolkit-docs-generator/data/toolkits/zoom.json b/toolkit-docs-generator/data/toolkits/zoom.json index fdcec99a1..d5272ead0 100644 --- a/toolkit-docs-generator/data/toolkits/zoom.json +++ b/toolkit-docs-generator/data/toolkits/zoom.json @@ -650,11 +650,11 @@ "subPages": [ { "type": "install", - "content": "# Arcade for Zoom\n\nimport { Steps, Callout } from \"nextra/components\";\nimport { SignupLink } from \"@/app/_components/analytics\";\nimport { ZoomAuthLink } from \"./zoom-auth-link\";\n\n## Integrate Arcade with your Zoom account\n\nArcade securely connects your AI agents to APIs, data, code, and other systems via Tools. Our Zoom integration allows Arcade's tools to connect to your Zoom account, helping you manage meetings and gather information more efficiently.\n\nYou can leverage this app in Arcade's Playground when you log in to the Arcade Dashboard, or in your own applications.\n\nWhile the Arcade app for Zoom does not directly expose a Large Language Model (LLM) to you, you will likely use Arcade's tools in conjunction with an LLM. When using LLMs, there's always potential to generate inaccurate responses, summaries, or other output.\n\nArcade's Zoom app brings Arcade's powerful AI tool-calling capabilities to your meeting management. The Arcade app for Zoom can:\n\n- List your upcoming meetings within the next 24 hours\n- Retrieve meeting invitation details for specific meetings\n- Find the participants and/or registrants for a specific meeting\n- and more!\n\nFor more details on what tools are available and what scopes they require, see the [Zoom MCP Server documentation](/resources/integrations/social-communication/zoom).\n\n<Callout type=\"info\">\n The Arcade Zoom app requires an active Arcade account. If you don't have one\n yet,{\" \"}\n <SignupLink linkLocation=\"docs:zoom-app-install\">sign up for free</SignupLink>\n .\n</Callout>\n\n## How it works\n\n<Steps>\n\n### Start using Arcade's Zoom tools\n\nUse Arcade's [tools for Zoom](/resources/integrations/social-communication/zoom) to:\n\n- List your upcoming meetings\n- Get meeting invitation details\n- Find meeting participants and registrants\n- and more!\n\nTry leveraging the Arcade Zoom tools in the Arcade Playground by [chatting with an LLM](https://api.arcade.dev/dashboard/playground/chat) asking, \"What meetings do I have scheduled today?\" or [executing Zoom tools directly](https://api.arcade.dev/dashboard/playground/execute?toolId=ListUpcomingMeetings&toolkits=%5B%5D&authProviders=%5B%5D&secrets=%5B%5D&input=%7B%22user_id%22%3A%22me%22%7D) without interacting with an LLM.\n\n<Callout type=\"warning\">\n When using LLMs with Zoom, responses may sometimes contain inaccuracies.\n Always review AI-generated content before taking action.\n</Callout>\n\n</Steps>\n\n## Support and troubleshooting\n\nIf you encounter any issues connecting Arcade to your Zoom account:\n\n1. Verify you've granted all required permissions during authorization\n2. Ensure your Zoom account is active and in good standing\n3. Check that you're using a compatible browser (Chrome, Firefox, Safari, or Edge)\n4. Clear your browser cache and cookies, then try again\n\n### Adding the Arcade Zoom app to your Zoom account\n\nIf using the Arcade playground directly did not work, you can try adding the Arcade Zoom app to your Zoom account by clicking the \"Connect with Zoom\" button below.\n\n<ZoomAuthLink />\n\n<Callout type=\"warning\">\n You'll need to have a Zoom account with appropriate permissions to allow\n Arcade to access your Zoom data.\n</Callout>\n\n### Authorize the requested permissions\n\nWhen connecting Arcade to your Zoom account, depending on which Arcade tools you'll be using, you'll be asked to authorize specific permissions:\n\n- **user:read:user** - Allows Arcade to access basic profile information\n- **user:read:email** - Enables Arcade to access your email address\n- **meeting:read:meetings** - Enables Arcade to list your upcoming meetings\n- **meeting:read:invitation** - Enables Arcade to read meeting invitation details\n\nThese permissions ensure Arcade can perform the necessary functions while protecting your privacy and security.\n\n### Removing the Arcade Zoom app\n\nTo remove the Arcade Zoom app from your Zoom account, you can do so by going to the [Zoom App Marketplace](https://marketplace.zoom.us/user/installed) and uninstalling the app.\n\nArcade only stores authentication tokens, not your Zoom data. These tokens become invalid when you uninstall the app and will eventually expire. To remove tokens immediately, delete the Zoom Auth Provider from the [Arcade Dashboard](https://api.arcade.dev/dashboard/auth/oauth).\n\n## Privacy and security\n\nArcade takes the security of your Zoom data seriously:\n\n- We only request the minimum permissions needed for our tools to function\n- Your Zoom credentials are never stored on our servers\n- All communication between Arcade and Zoom is encrypted\n- You can revoke Arcade's access to your Zoom account at any time through your [Zoom App Marketplace](https://marketplace.zoom.us/user/installed)\n\n## Next steps\n\nThe Arcade Zoom app is a sample of what Arcade can do with your Zoom account. For your own applications, you might want to [create your own Zoom app](/references/auth-providers/zoom). Creating your own Zoom application will allow you to brand the app, customize the permissions, and more.\n\n## Need help?\n\nIf you have any questions or need assistance:\n\n- Check our [Zoom MCP Server documentation](/resources/integrations/social-communication/zoom)\n- [Contact our support team](/resources/contact-us)\n", + "content": "# Arcade for Zoom\n\nimport { Steps } from \"@/components/mdx/steps\";\nimport { Callout } from \"@/components/mdx/callout\";\nimport { SignupLink } from \"@/components/analytics\";\nimport { ZoomAuthLink } from \"./zoom-auth-link\";\n\n## Integrate Arcade with your Zoom account\n\nArcade securely connects your AI agents to APIs, data, code, and other systems via Tools. Our Zoom integration allows Arcade's tools to connect to your Zoom account, helping you manage meetings and gather information more efficiently.\n\nYou can leverage this app in Arcade's Playground when you log in to the Arcade Dashboard, or in your own applications.\n\nWhile the Arcade app for Zoom does not directly expose a Large Language Model (LLM) to you, you will likely use Arcade's tools in conjunction with an LLM. When using LLMs, there's always potential to generate inaccurate responses, summaries, or other output.\n\nArcade's Zoom app brings Arcade's powerful AI tool-calling capabilities to your meeting management. The Arcade app for Zoom can:\n\n- List your upcoming meetings within the next 24 hours\n- Retrieve meeting invitation details for specific meetings\n- Find the participants and/or registrants for a specific meeting\n- and more!\n\nFor more details on what tools are available and what scopes they require, see the [Zoom MCP Server documentation](/resources/integrations/social-communication/zoom).\n\n<Callout type=\"info\">\n The Arcade Zoom app requires an active Arcade account. If you don't have one\n yet,{\" \"}\n <SignupLink linkLocation=\"docs:zoom-app-install\">sign up for free</SignupLink>\n .\n</Callout>\n\n## How it works\n\n<Steps>\n\n### Start using Arcade's Zoom tools\n\nUse Arcade's [tools for Zoom](/resources/integrations/social-communication/zoom) to:\n\n- List your upcoming meetings\n- Get meeting invitation details\n- Find meeting participants and registrants\n- and more!\n\nTry leveraging the Arcade Zoom tools in the Arcade Playground by [chatting with an LLM](https://api.arcade.dev/dashboard/playground/chat) asking, \"What meetings do I have scheduled today?\" or [executing Zoom tools directly](https://api.arcade.dev/dashboard/playground/execute?toolId=ListUpcomingMeetings&toolkits=%5B%5D&authProviders=%5B%5D&secrets=%5B%5D&input=%7B%22user_id%22%3A%22me%22%7D) without interacting with an LLM.\n\n<Callout type=\"warning\">\n When using LLMs with Zoom, responses may sometimes contain inaccuracies.\n Always review AI-generated content before taking action.\n</Callout>\n\n</Steps>\n\n## Support and troubleshooting\n\nIf you encounter any issues connecting Arcade to your Zoom account:\n\n1. Verify you've granted all required permissions during authorization\n2. Ensure your Zoom account is active and in good standing\n3. Check that you're using a compatible browser (Chrome, Firefox, Safari, or Edge)\n4. Clear your browser cache and cookies, then try again\n\n### Adding the Arcade Zoom app to your Zoom account\n\nIf using the Arcade playground directly did not work, you can try adding the Arcade Zoom app to your Zoom account by clicking the \"Connect with Zoom\" button below.\n\n<ZoomAuthLink />\n\n<Callout type=\"warning\">\n You'll need to have a Zoom account with appropriate permissions to allow\n Arcade to access your Zoom data.\n</Callout>\n\n### Authorize the requested permissions\n\nWhen connecting Arcade to your Zoom account, depending on which Arcade tools you'll be using, you'll be asked to authorize specific permissions:\n\n- **user:read:user** - Allows Arcade to access basic profile information\n- **user:read:email** - Enables Arcade to access your email address\n- **meeting:read:meetings** - Enables Arcade to list your upcoming meetings\n- **meeting:read:invitation** - Enables Arcade to read meeting invitation details\n\nThese permissions ensure Arcade can perform the necessary functions while protecting your privacy and security.\n\n### Removing the Arcade Zoom app\n\nTo remove the Arcade Zoom app from your Zoom account, you can do so by going to the [Zoom App Marketplace](https://marketplace.zoom.us/user/installed) and uninstalling the app.\n\nArcade only stores authentication tokens, not your Zoom data. These tokens become invalid when you uninstall the app and will eventually expire. To remove tokens immediately, delete the Zoom Auth Provider from the [Arcade Dashboard](https://api.arcade.dev/dashboard/auth/oauth).\n\n## Privacy and security\n\nArcade takes the security of your Zoom data seriously:\n\n- We only request the minimum permissions needed for our tools to function\n- Your Zoom credentials are never stored on our servers\n- All communication between Arcade and Zoom is encrypted\n- You can revoke Arcade's access to your Zoom account at any time through your [Zoom App Marketplace](https://marketplace.zoom.us/user/installed)\n\n## Next steps\n\nThe Arcade Zoom app is a sample of what Arcade can do with your Zoom account. For your own applications, you might want to [create your own Zoom app](/references/auth-providers/zoom). Creating your own Zoom application will allow you to brand the app, customize the permissions, and more.\n\n## Need help?\n\nIf you have any questions or need assistance:\n\n- Check our [Zoom MCP Server documentation](/resources/integrations/social-communication/zoom)\n- [Contact our support team](/resources/contact-us)\n", "relativePath": "install/page.mdx" } ], - "curationSourceHash": "6007dc440877986aa2d1f0f7a108a94354c90a195ee571865830c7eeb1734c7b", + "curationSourceHash": "f09a4f51575c55c26a10c80e76bf2dabfd29a58e2b71bc12e07a4437a390d1da", "generatedAt": "2026-10-07T21:26:12.956Z", "summary": "Arcade's Zoom toolkit connects LLMs to a user's Zoom account, enabling agents to look up meetings, read transcripts and summaries, retrieve recordings, and inspect scheduling — all through a single authenticated connection.\n\n## Capabilities\n\n- **Meeting discovery & lookup:** Find meetings by phrase, period, or ID across scheduled, recurring (fixed and no-fixed-time), and cloud-recorded-only meetings; supports pagination and wide date ranges.\n- **Upcoming schedule inspection:** List meetings the authenticated user is joining or hosting within a configurable window up to 90 days, with clear handling of the host-only boundary and recurring series.\n- **Post-meeting content retrieval:** Read AI Companion summaries and full transcripts (from cloud recordings or the standalone AI Companion transcript, whichever is available) for any past meeting.\n- **Recording access:** Retrieve share links, passcodes, and per-file play/download URLs for cloud recordings to forward to anyone who missed a meeting.\n- **Invitation text:** Fetch Zoom's ready-to-paste invitation note for any scheduled meeting.\n- **Identity resolution:** Resolve the authenticated user's identity to ground possessive references (\"my meetings\") before acting on them.\n\n## OAuth\n\nThis toolkit uses OAuth 2.0 via the **Zoom** provider. See the [Arcade Zoom auth provider docs](https://docs.arcade.dev/en/references/auth-providers/zoom) for setup details." } \ No newline at end of file diff --git a/toolkit-docs-generator/scripts/README-sync-toolkit-sidebar.md b/toolkit-docs-generator/scripts/README-sync-toolkit-sidebar.md index bc7574504..893139b56 100644 --- a/toolkit-docs-generator/scripts/README-sync-toolkit-sidebar.md +++ b/toolkit-docs-generator/scripts/README-sync-toolkit-sidebar.md @@ -6,26 +6,56 @@ This script synchronizes the sidebar navigation with available toolkit JSON data ```bash # Run the sync (updates sidebar navigation) -npx tsx toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts +bun toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts # Dry run (shows what would change without making changes) -npx tsx toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts --dry-run +bun toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts --dry-run # Verbose output -npx tsx toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts --verbose +bun toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts --verbose # Both flags -npx tsx toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts --dry-run --verbose +bun toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts --dry-run --verbose ``` ## What it does 1. Reads toolkit JSON files from `toolkit-docs-generator/data/toolkits/`. 2. Maps toolkits to categories using the design system catalog. -3. Adds the partner integrations from `app/_data/partner-toolkits.ts`. -4. Creates or updates `_meta.tsx` files for each category folder. +3. Adds the partner integrations from `data/partner-toolkits.ts`. +4. Creates or updates the Fumadocs `meta.json` file in each category folder + under `content/docs/resources/integrations/<category>/`. 5. Skips toolkits without a recognized integration category. -6. Updates the main integrations `_meta.tsx`. +6. Checks that the main integrations `meta.json` lists every active category. + That file is hand-authored (it also carries the overview page and the + feedback section), so the script reports a missing category instead of + rewriting the file. + +## Output format + +Each category `meta.json` is a Fumadocs folder meta file: + +```json +{ + "title": "Search Tools", + "pages": [ + "---Optimized---", + "[Glean](/en/resources/integrations/search/glean)", + "---Starter---", + "[Exa API](/en/resources/integrations/search/exa-api)", + "---Partners---", + "nimble", + "tavily" + ] +} +``` + +- `---Title---` entries are section separators. +- `[Label](href)` entries are links. Generated toolkit pages are rendered from + JSON by `lib/toolkit-route.tsx`, not from MDX files in the folder, so they + have to be listed as links. +- Bare slugs (`nimble`, `tavily`) are authored MDX pages in the same folder. + Fumadocs reads their title from the page frontmatter. ## When to run @@ -34,24 +64,25 @@ Run this script when: - Adding a new toolkit JSON file to `toolkit-docs-generator/data/toolkits/` - Removing a toolkit JSON file - Updating toolkit categories in the design system -- Adding or removing a partner in `app/_data/partner-toolkits.ts` +- Adding or removing a partner in `data/partner-toolkits.ts` - Regenerating toolkit documentation ## Partner integrations Partner integrations (remote MCP Servers offered by Arcade partners) have hand-authored pages and no toolkit JSON file. This script reads them from -`app/_data/partner-toolkits.ts`, the same list the integrations catalog renders -its cards from. Each one lands in a `Partners` section at the end of its -category sidebar, keyed by the last segment of its `relativeDocsLink`. +`data/partner-toolkits.ts`, the same list the integrations catalog renders its +cards from. Each one lands in a `Partners` section at the end of its category +sidebar, listed by the last segment of its `relativeDocsLink`, which must match +the MDX file name in the category folder. -This script rewrites every category `_meta.tsx` from scratch, so the next run +This script rewrites every category `meta.json` from scratch, so the next run drops a partner entry that someone typed into one of those files by hand. Add -the partner to `app/_data/partner-toolkits.ts` and re-run the script instead. +the partner to `data/partner-toolkits.ts` and re-run the script instead. `tests/partner-integration-nav.test.ts` fails when a partner has no page or no sidebar entry. The script also refuses to run when a partner and a toolkit -resolve to the same slug in the same category, since the sidebar can hold only -one entry per key. +resolve to the same slug in the same category, since one URL cannot serve both +a generated page and an MDX page. ## Category mapping @@ -73,14 +104,21 @@ Toolkits are mapped to categories based on `@arcadeai/design-system` and Toolkits with an unrecognized category fail loudly instead of being routed to a catch-all bucket. +## Removing empty categories + +Pass `--remove-empty-sections` to delete a category folder that no longer has +any toolkits. Only a folder that holds nothing but the generated `meta.json` is +removed; a folder that still contains authored MDX files is reported and left +in place. + ## Testing ```bash # Run tests -pnpm vitest run toolkit-docs-generator/tests/scripts/sync-toolkit-sidebar.test.ts +bunx vitest run toolkit-docs-generator/tests/scripts/sync-toolkit-sidebar.test.ts # Watch mode -pnpm vitest watch toolkit-docs-generator/tests/scripts/sync-toolkit-sidebar.test.ts +bunx vitest watch toolkit-docs-generator/tests/scripts/sync-toolkit-sidebar.test.ts ``` ## Output @@ -90,7 +128,8 @@ The script prints a summary of changes: ```text === Toolkit Sidebar Sync Results === -Total toolkits: 96 +Total toolkits: 124 +Partner integrations: 2 Categories created (1): + sales diff --git a/toolkit-docs-generator/scripts/check-stale-summaries.ts b/toolkit-docs-generator/scripts/check-stale-summaries.ts index b9161ca35..1883a5397 100644 --- a/toolkit-docs-generator/scripts/check-stale-summaries.ts +++ b/toolkit-docs-generator/scripts/check-stale-summaries.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env tsx +#!/usr/bin/env bun /** * Check stale summaries @@ -9,7 +9,7 @@ * the script can gate CI or a local sanity check. * * Usage: - * pnpm dlx tsx toolkit-docs-generator/scripts/check-stale-summaries.ts + * bun toolkit-docs-generator/scripts/check-stale-summaries.ts */ import { readdirSync, readFileSync } from "node:fs"; @@ -61,7 +61,7 @@ const collectStaleSummaries = ( console.error(`✗ Cannot parse ${file}: ${result.error}`); throw new Error(`Cannot parse ${file}`); } - const toolkit = result.toolkit; + const { toolkit } = result; if (toolkit.summaryStale === true) { stale.push({ file, diff --git a/toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts b/toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts index 2e80f91c2..079396069 100644 --- a/toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts +++ b/toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts @@ -1,22 +1,24 @@ -#!/usr/bin/env node +#!/usr/bin/env bun /** * Script to synchronize toolkit sidebar navigation with available JSON data files. * * This script: * 1. Reads all toolkit JSON files from data/toolkits/ * 2. Maps each toolkit to its category from the design system - * 3. Adds the partner integrations from app/_data/partner-toolkits.ts, which + * 3. Adds the partner integrations from data/partner-toolkits.ts, which * have hand-authored pages and no JSON file of their own - * 4. Creates/updates _meta.tsx files for each category + * 4. Creates/updates the Fumadocs `meta.json` file for each category under + * content/docs/resources/integrations/<category>/ * 5. Skips toolkits without a recognized category - * 6. Updates the main integrations _meta.tsx if needed + * 6. Checks that the main integrations `meta.json` (owned by the content + * authors, never rewritten here) lists every active category * * Usage: - * npx tsx toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts - * npx tsx toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts --dry-run - * npx tsx toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts --verbose - * npx tsx toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts --remove-empty-sections - * npx tsx toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts --remove-empty-sections=false + * bun toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts + * bun toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts --dry-run + * bun toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts --verbose + * bun toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts --remove-empty-sections + * bun toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts --remove-empty-sections=false */ import { @@ -33,7 +35,7 @@ import { TOOLKITS as DESIGN_SYSTEM_TOOLKITS } from "@arcadeai/design-system/meta import { PARTNER_TOOLKITS, type PartnerToolkit, -} from "../../app/_data/partner-toolkits"; +} from "../../data/partner-toolkits"; import { resolveToolkitDataDir } from "../src/shared/toolkit-data-dir"; import { getToolkitSlug, @@ -68,30 +70,31 @@ const PROJECT_ROOT = resolve(__dirname, "..", ".."); // Configuration const CONFIG = { dataDir: resolveToolkitDataDir(), - integrationsDir: join(PROJECT_ROOT, "app/en/resources/integrations"), integrationsBasePath: "/en/resources/integrations", + integrationsDir: join(PROJECT_ROOT, "content/docs/resources/integrations"), }; +/** File name Fumadocs reads folder navigation from. */ +export const META_FILE_NAME = "meta.json"; + // Known categories with display names -const CATEGORY_NAMES: Record<string, string> = { - productivity: "Productivity & Docs", - development: "Developer Tools", - social: "Social & Communication", - databases: "Databases", +export const CATEGORY_NAMES: Record<string, string> = { "customer-support": "Customer Support", - search: "Search Tools", - sales: "Sales", + databases: "Databases", + development: "Developer Tools", entertainment: "Entertainment", payments: "Payments & Finance", + productivity: "Productivity & Docs", + sales: "Sales", + search: "Search Tools", + social: "Social & Communication", }; -// Category order for main _meta.tsx -const CATEGORY_ORDER: readonly string[] = INTEGRATION_CATEGORIES; - const CAPITAL_LETTER_REGEX = /([A-Z])/g; const FIRST_CHARACTER_REGEX = /^./; - -const IDENTIFIER_KEY_REGEX = /^[A-Za-z_$][A-Za-z0-9_$]*$/; +const JSON_INDENT = 2; +const EXTRACT_PREFIX_REGEX = /^\.\.\./; +const EXCLUDE_PREFIX_REGEX = /^!/; /** * This script only reads a handful of fields off each toolkit JSON file (it @@ -106,14 +109,6 @@ type ToolkitJson = Partial<Pick<MergedToolkit, "id" | "label">> & { >; }; -function renderObjectKey(key: string): string { - if (IDENTIFIER_KEY_REGEX.test(key)) { - return key; - } - const escaped = key.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); - return `"${escaped}"`; -} - export type ToolkitInfo = { id: string; slug: string; @@ -128,6 +123,17 @@ export type CategoryData = { toolkits: ToolkitInfo[]; }; +/** + * The shape of a Fumadocs `meta.json` this script writes. `pages` holds, in + * order: `---Title---` separators, `[Label](href)` link entries for generated + * toolkit pages, and bare slugs for authored MDX pages (partners) that live in + * the same folder. + */ +export type CategoryMeta = { + title: string; + pages: string[]; +}; + export type SyncResult = { categoriesUpdated: string[]; categoriesCreated: string[]; @@ -277,8 +283,8 @@ function resolveToolkitInfo( const jsonData = readToolkitJson(dataDir, slug); const toolkitId = jsonData?.id ?? slug; const docsSlug = getToolkitSlug({ - id: toolkitId, docsLink: jsonData?.metadata?.docsLink ?? null, + id: toolkitId, }); const designSystemToolkit = TOOLKITS.find( (t) => t.id.toLowerCase() === toolkitId.toLowerCase() @@ -305,13 +311,13 @@ function resolveToolkitInfo( const typeFromJson = jsonData?.metadata?.type ?? null; const info: ToolkitInfo = { + category, id: toolkitId, - slug: docsSlug, // Prefer the Design System label (has correct spacing/casing), but fall back // to JSON label for toolkits not present in the design system. label: labelFromDesignSystem ?? labelFromJson ?? getToolkitLabel(toolkitId), - category, navGroup: inferNavGroup(toolkitId, typeFromJson), + slug: docsSlug, }; const priority = designSystemToolkit ? 2 : 0; @@ -351,19 +357,20 @@ export type PartnerNavSource = Pick< * Sidebar entries for the partner integrations (remote MCP Servers offered by * Arcade partners). * - * Partner pages are hand-authored and have no JSON file in the data directory, - * so `buildToolkitInfoList` can't see them. This script rewrites every - * category's `_meta.tsx` from scratch, so an entry typed into that file by hand - * disappears on the next run. Deriving the entries from `PARTNER_TOOLKITS` — the - * same list the integrations catalog renders its cards from — keeps the sidebar - * and the catalog agreeing, and makes adding a partner a one-line change in one - * file. tests/partner-integration-nav.test.ts holds the two together. + * Partner pages are hand-authored MDX files in the category folder and have no + * JSON file in the data directory, so `buildToolkitInfoList` can't see them. + * This script rewrites every category's `meta.json` from scratch, so an entry + * typed into that file by hand disappears on the next run. Deriving the entries + * from `PARTNER_TOOLKITS` — the same list the integrations catalog renders its + * cards from — keeps the sidebar and the catalog agreeing, and makes adding a + * partner a one-line change in one file. tests/partner-integration-nav.test.ts + * holds the two together. */ export function buildPartnerToolkitInfoList( partners: readonly PartnerNavSource[] = PARTNER_TOOLKITS ): ToolkitInfo[] { return partners.map((partner) => { - const category = partner.category; + const { category } = partner; if (!(INTEGRATION_CATEGORIES as readonly string[]).includes(category)) { throw new Error( `Unrecognized integration category "${category}" for partner "${partner.id}".` @@ -371,23 +378,22 @@ export function buildPartnerToolkitInfoList( } return { + category, id: partner.id, + label: partner.label, + navGroup: "partner" as const, slug: getToolkitSlug({ - id: partner.id, docsLink: partner.relativeDocsLink, + id: partner.id, }), - label: partner.label, - category, - navGroup: "partner" as const, }; }); } /** * A partner and a JSON-backed toolkit that resolve to the same slug in the same - * category would render two `_meta.tsx` entries under one key. Fail here, naming - * both, rather than write a file that tsc rejects with a line number and no - * explanation of where the second entry came from. + * category would claim one URL for two pages. Fail here, naming both, rather + * than write a sidebar whose link and MDX entry point at the same path. */ export function mergeToolkitAndPartnerInfo( toolkits: ToolkitInfo[], @@ -406,7 +412,7 @@ export function mergeToolkitAndPartnerInfo( if (toolkitId) { throw new Error( `Partner "${partner.id}" and toolkit "${toolkitId}" both resolve to ${key}. ` + - "Give one of them a different slug, or drop the partner from app/_data/partner-toolkits.ts." + "Give one of them a different slug, or drop the partner from data/partner-toolkits.ts." ); } } @@ -423,7 +429,7 @@ export function groupByCategory( const grouped = new Map<string, ToolkitInfo[]>(); for (const toolkit of toolkits) { - const category = toolkit.category; + const { category } = toolkit; if (!grouped.has(category)) { grouped.set(category, []); } @@ -443,14 +449,36 @@ export function groupByCategory( return grouped; } +/** Fumadocs separator entry: `---Title---`. */ +export const renderSeparatorEntry = (title: string): string => `---${title}---`; + /** - * Generate _meta.tsx content for a category + * Fumadocs link entry: `[Label](href)`. The parser reads the label up to the + * first `]`, so a label containing one cannot round-trip; fail loudly instead + * of writing an entry Fumadocs would silently drop. */ -export function generateCategoryMeta( +export const renderLinkEntry = (label: string, href: string): string => { + if (label.includes("]")) { + throw new Error( + `Sidebar label "${label}" contains "]", which Fumadocs link entries cannot encode.` + ); + } + return `[${label}](${href})`; +}; + +/** + * Build the Fumadocs `meta.json` object for a category. + * + * Generated toolkit pages are not MDX files in the folder, so they are listed + * as `[Label](href)` links. Partner pages are authored MDX files in the same + * folder, so they are listed by bare slug and Fumadocs reads their title from + * the page's frontmatter. + */ +export function buildCategoryMeta( toolkits: ToolkitInfo[], category: string, integrationsBasePath: string -): string { +): CategoryMeta { const byLabel = (a: ToolkitInfo, b: ToolkitInfo) => a.label.localeCompare(b.label); @@ -464,128 +492,201 @@ export function generateCategoryMeta( .filter((t) => t.navGroup === "partner") .sort(byLabel); - const renderEntry = (t: ToolkitInfo) => { - // Escape any quotes in the label - const escapedLabel = t.label.replace(/"/g, '\\"'); - return ` ${renderObjectKey(t.slug)}: { - title: "${escapedLabel}", - href: "${integrationsBasePath}/${category}/${t.slug}", - }`; - }; + const renderLink = (t: ToolkitInfo) => + renderLinkEntry(t.label, `${integrationsBasePath}/${category}/${t.slug}`); - const renderSeparator = (title: string) => { - const key = `-- ${title}`; - return ` ${renderObjectKey(key)}: { - type: "separator", - title: "${title}", - }`; - }; - - const sections: string[] = []; + const pages: string[] = []; if (optimized.length > 0) { - sections.push(renderSeparator("Optimized")); - sections.push(...optimized.map(renderEntry)); + pages.push(renderSeparatorEntry("Optimized")); + pages.push(...optimized.map(renderLink)); } if (starter.length > 0) { - sections.push(renderSeparator("Starter")); - sections.push(...starter.map(renderEntry)); + pages.push(renderSeparatorEntry("Starter")); + pages.push(...starter.map(renderLink)); } if (partners.length > 0) { - sections.push(renderSeparator("Partners")); - sections.push(...partners.map(renderEntry)); + pages.push(renderSeparatorEntry("Partners")); + pages.push(...partners.map((t) => t.slug)); + } + + return { pages, title: CATEGORY_NAMES[category] ?? category }; +} + +/** + * Generate `meta.json` content for a category + */ +export function generateCategoryMeta( + toolkits: ToolkitInfo[], + category: string, + integrationsBasePath: string +): string { + const meta = buildCategoryMeta(toolkits, category, integrationsBasePath); + return `${JSON.stringify(meta, null, JSON_INDENT)}\n`; +} + +/** + * The main integrations `meta.json` is authored by hand (it also carries the + * overview page and the feedback section), so this script never rewrites it. + * It does check that every active category is reachable from it: a category + * folder that the parent `pages` list omits renders no sidebar entries at all, + * which is exactly the silent 404 class of bug the sync exists to prevent. + * + * Returns the active categories missing from `pages`. A `pages` list that + * contains the `...` rest marker includes every folder, so nothing is missing. + */ +export function findMissingMainMetaCategories( + mainMeta: { pages?: unknown } | null, + activeCategories: readonly string[] +): string[] { + const pages = Array.isArray(mainMeta?.pages) + ? mainMeta.pages.filter( + (entry): entry is string => typeof entry === "string" + ) + : []; + + if (pages.includes("...") || pages.includes("z...a")) { + return []; } - const entries = sections.join(",\n"); + const listed = new Set( + pages.map((entry) => + entry.replace(EXTRACT_PREFIX_REGEX, "").replace(EXCLUDE_PREFIX_REGEX, "") + ) + ); + return activeCategories.filter( + (category) => + (INTEGRATION_CATEGORIES as readonly string[]).includes(category) && + !listed.has(category) + ); +} - return `import type { MetaRecord } from "nextra"; +function readMainMeta(metaPath: string): { pages?: unknown } | null { + if (!existsSync(metaPath)) { + return null; + } + try { + return JSON.parse(readFileSync(metaPath, "utf-8")) as { pages?: unknown }; + } catch (error) { + throw new Error( + `Main integrations ${META_FILE_NAME} at ${metaPath} is not valid JSON: ${String(error)}`, + { cause: error } + ); + } +} -const meta: MetaRecord = { -${entries}, +type SyncContext = { + dryRun: boolean; + log: (msg: string) => void; + result: SyncResult; }; -export default meta; -`; +function writeCategoryMeta( + category: string, + categoryToolkits: ToolkitInfo[], + { dryRun, log, result }: SyncContext +): void { + const categoryDir = join(CONFIG.integrationsDir, category); + const metaPath = join(categoryDir, META_FILE_NAME); + const metaContent = generateCategoryMeta( + categoryToolkits, + category, + CONFIG.integrationsBasePath + ); + + if (!existsSync(categoryDir)) { + log(`Creating category directory: ${category}`); + if (!dryRun) { + mkdirSync(categoryDir, { recursive: true }); + } + result.categoriesCreated.push(category); + } + + // Check if content changed + const contentChanged = existsSync(metaPath) + ? readFileSync(metaPath, "utf-8") !== metaContent + : true; + + if (!contentChanged) { + log(`No changes for: ${category}`); + return; + } + + log( + `Updating ${META_FILE_NAME} for: ${category} (${categoryToolkits.length} toolkits)` + ); + if (!dryRun) { + writeFileSync(metaPath, metaContent); + } + if (!result.categoriesCreated.includes(category)) { + result.categoriesUpdated.push(category); + } } /** - * Generate main integrations _meta.tsx content + * Category directories under the integrations folder: every directory whose + * name is an integration category (current or former — a stale `others/` + * folder is still a category directory this script once wrote). */ -export function generateMainMeta(activeCategories: string[]): string { - const supportedCategories = activeCategories.filter((category) => - (INTEGRATION_CATEGORIES as readonly string[]).includes(category) - ); +function listExistingCategoryDirs(): string[] { + if (!existsSync(CONFIG.integrationsDir)) { + return []; + } + return readdirSync(CONFIG.integrationsDir, { withFileTypes: true }) + .filter((d) => d.isDirectory()) + .map((d) => d.name) + .filter( + (name) => + name in CATEGORY_NAMES || + (INTEGRATION_CATEGORIES as readonly string[]).includes(name) + ); +} - // Sort categories by defined order - const sortedCategories = [...supportedCategories].sort((a, b) => { - const indexA = CATEGORY_ORDER.indexOf(a); - const indexB = CATEGORY_ORDER.indexOf(b); - if (indexA === -1 && indexB === -1) { - return a.localeCompare(b); +/** + * Remove a category folder that no longer has toolkits. Only a folder holding + * nothing but the generated `meta.json` is deleted: authored MDX next to it is + * someone's content, so that folder is left alone and reported instead. + */ +function removeEmptyCategoryDirs( + existingDirs: string[], + activeCategories: string[], + { dryRun, log, result }: SyncContext +): void { + for (const existingDir of existingDirs) { + if (activeCategories.includes(existingDir)) { + continue; } - if (indexA === -1) { - return 1; + const categoryDir = join(CONFIG.integrationsDir, existingDir); + const leftovers = readdirSync(categoryDir).filter( + (name) => name !== META_FILE_NAME + ); + if (leftovers.length > 0) { + result.errors.push( + `Category "${existingDir}" has no toolkits but still contains authored files (${leftovers.join(", ")}); not removing it.` + ); + continue; } - if (indexB === -1) { - return -1; + log(`Removing empty category: ${existingDir}`); + if (!dryRun) { + rmSync(categoryDir, { recursive: true }); } - return indexA - indexB; - }); - - const categoryEntries = sortedCategories - .map((cat) => { - const displayName = CATEGORY_NAMES[cat] || cat; - return ` ${renderObjectKey(cat)}: { - title: "${displayName}", - }`; - }) - .join(",\n"); - - return `import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "*": { - theme: { - breadcrumb: true, - toc: true, - copyPage: true, - }, - }, - index: { - title: "Overview", - theme: { - toc: false, - copyPage: false, - }, - }, -${categoryEntries}, - "-- Feedback": { - type: "separator", - title: "Feedback", - }, - "tool-feedback": { - title: "Tool Feedback", - }, -}; - -export default meta; -`; + result.categoriesRemoved.push(existingDir); + } } /** * Sync toolkit sidebar with available JSON files */ -// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Main orchestration function requires multiple steps export function syncToolkitSidebar(options: SyncOptions = {}): SyncResult { const { dryRun = false, verbose = false } = options; const removeEmptySections = resolveRemoveEmptySections(options); const result: SyncResult = { - categoriesUpdated: [], categoriesCreated: [], categoriesRemoved: [], - toolkitCount: 0, - partnerCount: 0, + categoriesUpdated: [], errors: [], + partnerCount: 0, + toolkitCount: 0, }; const log = (msg: string) => { @@ -593,6 +694,7 @@ export function syncToolkitSidebar(options: SyncOptions = {}): SyncResult { console.log(msg); } }; + const context: SyncContext = { dryRun, log, result }; try { // Build toolkit info from JSON files @@ -611,93 +713,35 @@ export function syncToolkitSidebar(options: SyncOptions = {}): SyncResult { const activeCategories = Array.from(grouped.keys()); log(`Active categories: ${activeCategories.join(", ")}`); - // Get existing category directories - const existingDirs = existsSync(CONFIG.integrationsDir) - ? readdirSync(CONFIG.integrationsDir, { withFileTypes: true }) - .filter((d) => d.isDirectory()) - .filter( - (d) => !["components", "tool-feedback", "_lib"].includes(d.name) - ) - .map((d) => d.name) - : []; - - // Create/update category directories - for (const [category, categoryToolkits] of grouped) { - const categoryDir = join(CONFIG.integrationsDir, category); - const metaPath = join(categoryDir, "_meta.tsx"); - const metaContent = generateCategoryMeta( - categoryToolkits, - category, - CONFIG.integrationsBasePath - ); - - const dirExists = existsSync(categoryDir); - const metaExists = existsSync(metaPath); - - if (!dirExists) { - log(`Creating category directory: ${category}`); - if (!dryRun) { - mkdirSync(categoryDir, { recursive: true }); - } - result.categoriesCreated.push(category); - } + // Snapshot existing category directories before writing new ones + const existingDirs = listExistingCategoryDirs(); - // Check if content changed - let contentChanged = true; - if (metaExists) { - const existingContent = readFileSync(metaPath, "utf-8"); - contentChanged = existingContent !== metaContent; - } - - if (contentChanged) { - log( - `Updating _meta.tsx for: ${category} (${categoryToolkits.length} toolkits)` - ); - if (!dryRun) { - writeFileSync(metaPath, metaContent); - } - if (!result.categoriesCreated.includes(category)) { - result.categoriesUpdated.push(category); - } - } else { - log(`No changes for: ${category}`); - } + // Create/update category meta.json files + for (const [category, categoryToolkits] of grouped) { + writeCategoryMeta(category, categoryToolkits, context); } // Remove category directories only when explicitly enabled. // This prevents automatic route removals during regular sync runs. if (removeEmptySections) { - for (const existingDir of existingDirs) { - if (!activeCategories.includes(existingDir)) { - const categoryDir = join(CONFIG.integrationsDir, existingDir); - log(`Removing empty category: ${existingDir}`); - if (!dryRun) { - rmSync(categoryDir, { recursive: true }); - } - result.categoriesRemoved.push(existingDir); - } - } + removeEmptyCategoryDirs(existingDirs, activeCategories, context); } else { log("Skipping empty category removal (--remove-empty-sections=false)"); } - // Update main _meta.tsx - const mainMetaPath = join(CONFIG.integrationsDir, "_meta.tsx"); - const mainMetaContent = generateMainMeta(activeCategories); - - if (existsSync(mainMetaPath)) { - const existingMainMeta = readFileSync(mainMetaPath, "utf-8"); - if (existingMainMeta !== mainMetaContent) { - log("Updating main _meta.tsx"); - if (!dryRun) { - writeFileSync(mainMetaPath, mainMetaContent); - } + // Check the hand-authored main meta.json lists every active category + const mainMetaPath = join(CONFIG.integrationsDir, META_FILE_NAME); + const mainMeta = readMainMeta(mainMetaPath); + if (mainMeta) { + const missing = findMissingMainMetaCategories(mainMeta, activeCategories); + if (missing.length > 0) { + result.errors.push( + `Main integrations ${META_FILE_NAME} (${mainMetaPath}) does not list: ${missing.join(", ")}. ` + + "Add each category to its `pages` array so the sidebar can reach it." + ); } } else { - log("Creating main _meta.tsx"); - if (!dryRun) { - writeFileSync(mainMetaPath, mainMetaContent); - } + log(`No main ${META_FILE_NAME} at ${mainMetaPath}; skipping check`); } } catch (error) { result.errors.push(String(error)); @@ -763,7 +807,7 @@ if (isMainModule) { console.log("Running in dry-run mode (no changes will be made)\n"); } - const result = syncToolkitSidebar({ dryRun, verbose, removeEmptySections }); + const result = syncToolkitSidebar({ dryRun, removeEmptySections, verbose }); printResults(result); if (result.errors.length > 0) { diff --git a/toolkit-docs-generator/src/merger/data-merger.ts b/toolkit-docs-generator/src/merger/data-merger.ts index 833d89407..af6147839 100644 --- a/toolkit-docs-generator/src/merger/data-merger.ts +++ b/toolkit-docs-generator/src/merger/data-merger.ts @@ -47,7 +47,7 @@ import { // Merger Configuration // ============================================================================ -export interface DataMergerConfig { +export type DataMergerConfig = { toolkitDataSource: ToolkitDataSource; customSectionsSource: ICustomSectionsSource; toolExampleGenerator?: ToolExampleGenerator; @@ -87,16 +87,16 @@ export interface DataMergerConfig { preserveLastKnownGood?: boolean; /** Fallback resolver: toolkit ID → OAuth provider ID (design system) */ resolveProviderId?: ((toolkitId: string) => string | null) | undefined; -} +}; -export interface FailedTool { +export type FailedTool = { readonly toolkitId: string; readonly toolName: string; readonly qualifiedName: string; readonly reason: string; -} +}; -export interface MergeResult { +export type MergeResult = { toolkit: MergedToolkit; warnings: string[]; failedTools: FailedTool[]; @@ -112,20 +112,20 @@ export interface MergeResult { * since that's easy to miss in a warnings list read only on failure. */ usedDefaultMetadata: boolean; -} +}; -export interface ToolExampleResult { +export type ToolExampleResult = { codeExample: ToolCodeExample; secretsInfo: MergedTool["secretsInfo"]; -} +}; -export interface ToolExampleGenerator { +export type ToolExampleGenerator = { generate: (tool: ToolDefinition) => Promise<ToolExampleResult>; -} +}; -export interface ToolkitSummaryGenerator { +export type ToolkitSummaryGenerator = { generate: (toolkit: MergedToolkit) => Promise<string>; -} +}; /** * Under `--require-complete`, every toolkit must have design-system metadata. @@ -146,13 +146,13 @@ export const assertRequireCompleteMetadata = ( } }; -interface MergeToolkitOptions { +type MergeToolkitOptions = { previousToolkit?: MergedToolkit | undefined; /** Maximum concurrent LLM calls for tool examples (default: 5) */ llmConcurrency?: number; /** Fallback resolver: toolkit ID → OAuth provider ID (design system) */ resolveProviderId?: (toolkitId: string) => string | null; -} +}; // ============================================================================ // Pure Functions for Data Transformation @@ -167,7 +167,7 @@ export const groupToolsByToolkit = ( const groups = new Map<string, ToolDefinition[]>(); for (const tool of tools) { - const toolkitName = tool.qualifiedName.split(".")[0]; + const [toolkitName] = tool.qualifiedName.split("."); if (!toolkitName) continue; const existing = groups.get(toolkitName) ?? []; @@ -499,7 +499,7 @@ const DEFAULT_METADATA_CATEGORY: MergedToolkitMetadata["category"] = * closed enum with no catch-all — so a wrong-but-valid category would * otherwise file the toolkit under the wrong sidebar section with a * canonical URL nobody chose. `isHidden: true` is what actually neutralizes - * that: `app/_lib/toolkit-static-params.ts` drops hidden toolkits from + * that: `lib/toolkit-static-params.ts` drops hidden toolkits from * routing entirely, so this placeholder record can exist on disk (and keep * CI green when metadata truly is optional) without ever rendering under * the wrong category. Real metadata — pulled in the next successful design @@ -697,14 +697,14 @@ const appendMergeWarnings = ( }; const getToolkitVersion = (tools: readonly ToolDefinition[]): string => { - const firstTool = tools[0]; + const [firstTool] = tools; return firstTool ? extractVersion(firstTool.fullyQualifiedName) : "0.0.0"; }; const getToolkitDescription = ( tools: readonly ToolDefinition[] ): string | null => { - const firstTool = tools[0]; + const [firstTool] = tools; return firstTool?.toolkitDescription ?? firstTool?.description ?? null; }; @@ -994,7 +994,8 @@ export const mergeToolkit = async ( options.resolveProviderId && tools.some( (tool) => - tool.auth?.providerType === "oauth2" && tool.auth.providerId == null + tool.auth?.providerType === "oauth2" && + (tool.auth.providerId === null || tool.auth.providerId === undefined) ) ? options.resolveProviderId(toolkitId) : null; @@ -1004,7 +1005,8 @@ export const mergeToolkit = async ( ? tools.map((tool) => { if ( tool.auth?.providerType === "oauth2" && - tool.auth.providerId == null + (tool.auth.providerId === null || + tool.auth.providerId === undefined) ) { return { ...tool, @@ -1477,7 +1479,7 @@ export class DataMerger { if (!editor) { return; } - const summary = result.toolkit.summary; + const { summary } = result.toolkit; if (!summary) { return; } diff --git a/toolkit-docs-generator/src/shared/auth-provider-pages.ts b/toolkit-docs-generator/src/shared/auth-provider-pages.ts index a2a384114..50abdd576 100644 --- a/toolkit-docs-generator/src/shared/auth-provider-pages.ts +++ b/toolkit-docs-generator/src/shared/auth-provider-pages.ts @@ -1,5 +1,5 @@ /** - * Auth provider pages that exist under app/en/references/auth-providers/, + * Auth provider pages that exist under content/docs/references/auth-providers/, * shared by the toolkit page header and the toolkit summary prompt so neither * links to a provider that has no page (for example `box`). * diff --git a/toolkit-docs-generator/src/shared/toolkit-data-dir.ts b/toolkit-docs-generator/src/shared/toolkit-data-dir.ts index d451605b8..a4a6eda31 100644 --- a/toolkit-docs-generator/src/shared/toolkit-data-dir.ts +++ b/toolkit-docs-generator/src/shared/toolkit-data-dir.ts @@ -20,7 +20,7 @@ import { join } from "node:path"; * repo root during `next build`/`next dev` and the function root at runtime, * and Next.js file tracing recognizes it well enough to ship the JSON. * - * Every entry point here (root `pnpm` scripts, CI workflows, Vitest) runs from + * Every entry point here (root `bun run` scripts, CI workflows, Vitest) runs from * the repo root; anything that doesn't can set `TOOLKIT_DATA_DIR`. */ export const DEFAULT_TOOLKIT_DATA_DIR = join( diff --git a/toolkit-docs-generator/src/shared/toolkit-primitives.ts b/toolkit-docs-generator/src/shared/toolkit-primitives.ts index 1764190d1..98004f4ca 100644 --- a/toolkit-docs-generator/src/shared/toolkit-primitives.ts +++ b/toolkit-docs-generator/src/shared/toolkit-primitives.ts @@ -1,5 +1,5 @@ /** - * Toolkit primitives shared by the Next.js docs app (app/_lib and its + * Toolkit primitives shared by the Next.js docs app (lib/ and its * consumers) and toolkit-docs-generator. Both halves need the same toolkit * ID/slug/category logic, but the generator's tsconfig pins `rootDir` to its * own `src/`, so a module outside that directory fails its build @@ -99,13 +99,14 @@ export function getToolkitSlug({ id, docsLink }: ToolkitSlugSource): string { /** * The docs-generation category buckets. Each value corresponds to exactly - * one `app/en/resources/integrations/<category>/[toolkitId]` route directory + * one generated `content/docs/resources/integrations/<category>/meta.json` + * sidebar folder, is served by `lib/toolkit-route.tsx` * (see tests/integration-category-routes.test.ts) and to the design system's * own `ToolkitCategory` union (minus its "all" filter meta-value) — see - * app/en/resources/integrations/components/filter-params.ts. There is + * components/pages/integrations/filter-params.ts. There is * deliberately no "others" catch-all: a toolkit whose category doesn't match * one of these has no page to render, so `normalizeCategory` in - * app/_lib/toolkit-static-params.ts throws instead of bucketing it here. + * lib/toolkit-static-params.ts throws instead of bucketing it here. * * `ToolkitCategorySchema` in ./toolkit-schemas.ts is built from this array, * so the generator's contract and the docs app's route set can't drift diff --git a/toolkit-docs-generator/src/shared/toolkit-schemas.ts b/toolkit-docs-generator/src/shared/toolkit-schemas.ts index 42f5a5f66..559648d9f 100644 --- a/toolkit-docs-generator/src/shared/toolkit-schemas.ts +++ b/toolkit-docs-generator/src/shared/toolkit-schemas.ts @@ -1,12 +1,12 @@ /** * Zod schemas for the merged toolkit JSON contract, shared by the Next.js - * docs app (app/_lib and app/_components/toolkit-docs) and + * docs app (lib/ and components/toolkit-docs) and * toolkit-docs-generator. The generator validates its own output against * these schemas on write (see src/generator/json-generator.ts); the app - * validates on read (see app/_lib/toolkit-data.ts) so a file that doesn't + * validates on read (see lib/toolkit-data.ts) so a file that doesn't * match this shape is rejected instead of crashing mid-render. * - * This lives under toolkit-docs-generator/src/ (not app/_lib or a repo-root + * This lives under toolkit-docs-generator/src/ (not lib/ or a repo-root * shared/ directory) because the generator's tsconfig pins `rootDir` to its * own `src/`, so a shared module outside that directory fails the * generator's build (`TS6059: File '...' is not under 'rootDir'`). The app diff --git a/toolkit-docs-generator/tests/diff/previous-output.test.ts b/toolkit-docs-generator/tests/diff/previous-output.test.ts index a59004108..95af8139d 100644 --- a/toolkit-docs-generator/tests/diff/previous-output.test.ts +++ b/toolkit-docs-generator/tests/diff/previous-output.test.ts @@ -181,7 +181,7 @@ describe("parsePreviousToolkitForDiff", () => { header: "## GitHub Enterprise Support", }, ], - customImports: ['import { Callout } from "nextra/components";'], + customImports: ['import { Callout } from "@/components/mdx/callout";'], subPages: [], }, "github" @@ -197,7 +197,7 @@ describe("parsePreviousToolkitForDiff", () => { ); expect(result.toolkit?.customImports).toHaveLength(1); expect(result.toolkit?.customImports[0]).toBe( - 'import { Callout } from "nextra/components";' + 'import { Callout } from "@/components/mdx/callout";' ); expect(result.toolkit?.subPages).toHaveLength(0); }); diff --git a/toolkit-docs-generator/tests/merger/data-merger.test.ts b/toolkit-docs-generator/tests/merger/data-merger.test.ts index 0d55d1664..2a4f19366 100644 --- a/toolkit-docs-generator/tests/merger/data-merger.test.ts +++ b/toolkit-docs-generator/tests/merger/data-merger.test.ts @@ -449,7 +449,7 @@ describe("mergeToolkit", () => { // isHidden neutralizes it until real design-system metadata arrives. expect(result.toolkit.metadata.isHidden).toBe(true); // Must match the real integration route shape (see - // app/_lib/toolkit-static-params.ts getToolkitCanonicalPath), never the + // lib/toolkit-static-params.ts getToolkitCanonicalPath), never the // retired /en/mcp-servers/ prefix. expect(result.toolkit.metadata.docsLink).toBe( "https://docs.arcade.dev/en/resources/integrations/development/unknown" @@ -2052,7 +2052,9 @@ describe("DataMerger", () => { content: "Critical: GitHub Apps only.", }, ], - customImports: ['import { Callout } from "nextra/components";'], + customImports: [ + 'import { Callout } from "@/components/mdx/callout";', + ], subPages: ["setup-guide"], }), createStubGenerator() diff --git a/toolkit-docs-generator/tests/scenarios/prose-survives-force-regenerate.test.ts b/toolkit-docs-generator/tests/scenarios/prose-survives-force-regenerate.test.ts index b7ee5c813..7bc1bdc74 100644 --- a/toolkit-docs-generator/tests/scenarios/prose-survives-force-regenerate.test.ts +++ b/toolkit-docs-generator/tests/scenarios/prose-survives-force-regenerate.test.ts @@ -60,7 +60,7 @@ type: environment-variables `--- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; ` ); }; @@ -98,7 +98,7 @@ describe("prose survives --force-regenerate", () => { "Hand-authored guidance that has no upstream source." ); expect(result.toolkit.customImports).toEqual([ - 'import StarterToolInfo from "@/app/_components/starter-tool-info";', + 'import StarterToolInfo from "@/components/starter-tool-info";', ]); expect(result.toolkit.subPages).toEqual([ { diff --git a/toolkit-docs-generator/tests/scripts/sync-toolkit-sidebar.test.ts b/toolkit-docs-generator/tests/scripts/sync-toolkit-sidebar.test.ts index 89efcc3db..e28bc3847 100644 --- a/toolkit-docs-generator/tests/scripts/sync-toolkit-sidebar.test.ts +++ b/toolkit-docs-generator/tests/scripts/sync-toolkit-sidebar.test.ts @@ -1,19 +1,21 @@ /** * Tests for sync-toolkit-sidebar.ts * - * Run with: npx vitest run tests/github-scripts/sync-toolkit-sidebar.test.ts - * Or: npx vitest watch tests/github-scripts/sync-toolkit-sidebar.test.ts + * Run with: bunx vitest run toolkit-docs-generator/tests/scripts/sync-toolkit-sidebar.test.ts + * Or: bunx vitest watch toolkit-docs-generator/tests/scripts/sync-toolkit-sidebar.test.ts */ import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; -import { getToolkitStaticParamsForCategory } from "../../../app/_lib/toolkit-static-params"; +import { getToolkitStaticParamsForCategory } from "../../../lib/toolkit-static-params"; import { + buildCategoryMeta, buildPartnerToolkitInfoList, buildToolkitInfoList, + type CategoryMeta, + findMissingMainMetaCategories, generateCategoryMeta, - generateMainMeta, getToolkitCategory, getToolkitFiles, getToolkitLabel, @@ -51,7 +53,10 @@ setToolkitsForTesting([ // Test directory setup const TEST_DIR = join(process.cwd(), ".test-sync-sidebar"); const TEST_DATA_DIR = join(TEST_DIR, "data/toolkits"); -const TEST_INTEGRATIONS_DIR = join(TEST_DIR, "app/en/resources/integrations"); +const TEST_INTEGRATIONS_DIR = join( + TEST_DIR, + "content/docs/resources/integrations" +); function setupTestDirs() { mkdirSync(TEST_DATA_DIR, { recursive: true }); @@ -64,6 +69,15 @@ function cleanupTestDirs() { } } +const parseMeta = (content: string): CategoryMeta => + JSON.parse(content) as CategoryMeta; + +const BRACKET_LABEL_ERROR_REGEX = /contains "\]"/; +const UNRECOGNIZED_ALL_CATEGORY_REGEX = + /Unrecognized integration category "all"/; +const PARTNER_SLUG_COLLISION_REGEX = + /Partner "Tavily" and toolkit "GoogleSearch" both resolve to search\/tavily/; + function createToolkitJson(slug: string, data: object = {}) { const filePath = join(TEST_DATA_DIR, `${slug}.json`); writeFileSync(filePath, JSON.stringify({ id: slug, ...data }, null, 2)); @@ -349,8 +363,8 @@ describe("buildToolkitInfoList", () => { weaviate.category, "/en/resources/integrations" ); - expect(sidebarMeta).toContain( - 'href: "/en/resources/integrations/databases/weaviate-api"' + expect(parseMeta(sidebarMeta).pages).toContain( + "[Weaviate API](/en/resources/integrations/databases/weaviate-api)" ); const toolkitsCatalog = [ @@ -529,7 +543,7 @@ describe("remove empty section flags", () => { // ============================================================================ describe("generateCategoryMeta", () => { - it("should generate valid _meta.tsx content", () => { + it("should generate valid Fumadocs meta.json content", () => { const toolkits: ToolkitInfo[] = [ { id: "gmail", @@ -553,17 +567,34 @@ describe("generateCategoryMeta", () => { "/en/resources/integrations" ); - expect(result).toContain('import type { MetaRecord } from "nextra"'); - expect(result).toContain("gmail: {"); - expect(result).toContain('title: "Gmail"'); - expect(result).toContain( - 'href: "/en/resources/integrations/productivity/gmail"' - ); - expect(result).toContain("dropbox: {"); - expect(result).toContain("export default meta"); + expect(result.endsWith("\n")).toBe(true); + expect(parseMeta(result)).toEqual({ + pages: [ + "---Optimized---", + "[Dropbox](/en/resources/integrations/productivity/dropbox)", + "[Gmail](/en/resources/integrations/productivity/gmail)", + ], + title: "Productivity & Docs", + }); }); - it("should escape quotes in labels", () => { + it("rejects a label Fumadocs link syntax cannot encode", () => { + const toolkits: ToolkitInfo[] = [ + { + category: "productivity", + id: "test", + label: "Test [Bracketed] Label", + navGroup: "optimized", + slug: "test", + }, + ]; + + expect(() => + generateCategoryMeta(toolkits, "productivity", "/preview") + ).toThrow(BRACKET_LABEL_ERROR_REGEX); + }); + + it("keeps quotes in labels intact through JSON encoding", () => { const toolkits: ToolkitInfo[] = [ { id: "test", @@ -576,15 +607,22 @@ describe("generateCategoryMeta", () => { const result = generateCategoryMeta(toolkits, "productivity", "/preview"); - expect(result).toContain('title: "Test \\"Quoted\\" Label"'); + expect(parseMeta(result).pages).toContain( + '[Test "Quoted" Label](/preview/productivity/test)' + ); }); it("should handle empty array", () => { const result = generateCategoryMeta([], "productivity", "/preview"); - expect(result).toContain("const meta: MetaRecord = {"); - expect(result).toContain("};"); - expect(result).toContain("export default meta"); + expect(parseMeta(result)).toEqual({ + pages: [], + title: "Productivity & Docs", + }); + }); + + it("falls back to the category id when it has no display name", () => { + expect(buildCategoryMeta([], "others", "/preview").title).toBe("others"); }); it("should handle single toolkit", () => { @@ -600,8 +638,10 @@ describe("generateCategoryMeta", () => { const result = generateCategoryMeta(toolkits, "productivity", "/preview"); - expect(result).toContain("gmail: {"); - expect(result).not.toContain(",\n,"); // No trailing comma issues + expect(parseMeta(result).pages).toEqual([ + "---Optimized---", + "[Gmail](/preview/productivity/gmail)", + ]); }); it("adds Optimized separator even when no starter entries exist", () => { @@ -622,16 +662,20 @@ describe("generateCategoryMeta", () => { }, ]; - const result = generateCategoryMeta( + const { pages } = buildCategoryMeta( toolkits, "entertainment", "/en/resources/integrations" ); - expect(result).toContain('"-- Optimized"'); - expect(result).not.toContain('"-- Starter"'); - expect(result).toContain("imgflip:"); - expect(result).toContain("spotify:"); + expect(pages).toContain("---Optimized---"); + expect(pages).not.toContain("---Starter---"); + expect(pages).toContain( + "[Imgflip](/en/resources/integrations/entertainment/imgflip)" + ); + expect(pages).toContain( + "[Spotify](/en/resources/integrations/entertainment/spotify)" + ); }); it("adds Starter separator even when no optimized entries exist", () => { @@ -645,15 +689,17 @@ describe("generateCategoryMeta", () => { }, ]; - const result = generateCategoryMeta( + const { pages } = buildCategoryMeta( toolkits, "productivity", "/en/resources/integrations" ); - expect(result).not.toContain('"-- Optimized"'); - expect(result).toContain('"-- Starter"'); - expect(result).toContain('"my-api":'); + expect(pages).not.toContain("---Optimized---"); + expect(pages).toEqual([ + "---Starter---", + "[My API](/en/resources/integrations/productivity/my-api)", + ]); }); it("adds both separators when both optimized and starter entries exist", () => { @@ -674,17 +720,16 @@ describe("generateCategoryMeta", () => { }, ]; - const result = generateCategoryMeta( + const { pages } = buildCategoryMeta( toolkits, "productivity", "/en/resources/integrations" ); - expect(result).toContain('"-- Optimized"'); - expect(result).toContain('"-- Starter"'); - const optimizedIndex = result.indexOf('"-- Optimized"'); - const starterIndex = result.indexOf('"-- Starter"'); - expect(optimizedIndex).toBeLessThan(starterIndex); + const optimizedIndex = pages.indexOf("---Optimized---"); + const starterIndex = pages.indexOf("---Starter---"); + expect(optimizedIndex).toBeGreaterThan(-1); + expect(starterIndex).toBeGreaterThan(optimizedIndex); }); it("does not mutate the input array", () => { @@ -713,69 +758,70 @@ describe("generateCategoryMeta", () => { }); // ============================================================================ -// Unit Tests: generateMainMeta +// Unit Tests: findMissingMainMetaCategories // ============================================================================ -describe("generateMainMeta", () => { - it("should generate main _meta.tsx with active categories", () => { - const result = generateMainMeta(["productivity", "social"]); +describe("findMissingMainMetaCategories", () => { + it("returns nothing when every active category is listed", () => { + const mainMeta = { + pages: [ + "index", + "productivity", + "social", + "---Feedback---", + "tool-feedback", + ], + }; - expect(result).toContain('import type { MetaRecord } from "nextra"'); - expect(result).toContain("productivity: {"); - expect(result).toContain('title: "Productivity & Docs"'); - expect(result).toContain("social: {"); - expect(result).toContain('title: "Social & Communication"'); - expect(result).toContain('"tool-feedback"'); - expect(result).not.toContain("preview:"); + expect( + findMissingMainMetaCategories(mainMeta, ["productivity", "social"]) + ).toEqual([]); }); - it("should sort categories by defined order", () => { - const result = generateMainMeta(["sales", "productivity", "development"]); - - const productivityIndex = result.indexOf("productivity:"); - const developmentIndex = result.indexOf("development:"); - const salesIndex = result.indexOf("sales:"); + it("names the active categories the main meta.json omits", () => { + const mainMeta = { pages: ["index", "productivity"] }; - expect(productivityIndex).toBeLessThan(developmentIndex); - expect(developmentIndex).toBeLessThan(salesIndex); + expect( + findMissingMainMetaCategories(mainMeta, [ + "sales", + "productivity", + "development", + ]) + ).toEqual(["sales", "development"]); }); - it("should omit the removed 'others' category", () => { - const result = generateMainMeta(["productivity", "others"]); - - const productivityIndex = result.indexOf("productivity:"); - const othersIndex = result.indexOf("others:"); - - expect(productivityIndex).toBeGreaterThan(-1); - expect(othersIndex).toBe(-1); + it("treats a rest marker as listing every folder", () => { + expect( + findMissingMainMetaCategories({ pages: ["index", "..."] }, ["sales"]) + ).toEqual([]); }); - it("should handle empty categories", () => { - const result = generateMainMeta([]); + it("ignores categories that are not integration categories", () => { + expect( + findMissingMainMetaCategories({ pages: ["index"] }, [ + "others", + "productivity", + ]) + ).toEqual(["productivity"]); + }); - expect(result).toContain("const meta: MetaRecord = {"); - expect(result).toContain("index:"); - expect(result).toContain('"tool-feedback"'); + it("treats a missing or malformed pages list as listing nothing", () => { + expect(findMissingMainMetaCategories(null, ["productivity"])).toEqual([ + "productivity", + ]); + expect( + findMissingMainMetaCategories({ pages: "productivity" }, ["productivity"]) + ).toEqual(["productivity"]); }); it("does not mutate the input array", () => { const categories = ["sales", "productivity", "development"]; const originalOrder = [...categories]; - generateMainMeta(categories); + findMissingMainMetaCategories({ pages: [] }, categories); expect(categories).toEqual(originalOrder); }); - - it("should include required structure elements", () => { - const result = generateMainMeta(["productivity"]); - - expect(result).toContain('"*":'); // Theme config - expect(result).toContain("index:"); // Overview - expect(result).toContain('"-- Feedback"'); // Separator - expect(result).toContain('"tool-feedback"'); // Feedback page - expect(result).not.toContain("preview:"); // Preview page removed - }); }); // ============================================================================ @@ -897,7 +943,7 @@ describe("category move cleanup logic", () => { }); it("generateCategoryMeta only includes current toolkits (not stale ones)", () => { - // After Pylon moves, the "others" _meta.tsx should only contain remaining toolkits + // After Pylon moves, the "others" meta.json should only contain remaining toolkits const othersToolkits: ToolkitInfo[] = [ { id: "CustomTool", @@ -918,13 +964,15 @@ describe("category move cleanup logic", () => { expect(meta).not.toContain("pylon"); }); - it("main _meta.tsx excludes categories with zero toolkits", () => { + it("a category with zero toolkits is flagged only when the main meta.json still lists it", () => { const activeCategories = ["development", "productivity"]; - const mainMeta = generateMainMeta(activeCategories); - expect(mainMeta).toContain("development:"); - expect(mainMeta).toContain("productivity:"); - expect(mainMeta).not.toContain("others:"); + expect( + findMissingMainMetaCategories( + { pages: ["development", "productivity", "others"] }, + activeCategories + ) + ).toEqual([]); }); }); @@ -959,7 +1007,7 @@ describe("buildPartnerToolkitInfoList", () => { it("throws on a category with no integrations route", () => { expect(() => buildPartnerToolkitInfoList([{ ...tavilyPartner, category: "all" }]) - ).toThrow(/Unrecognized integration category "all"/); + ).toThrow(UNRECOGNIZED_ALL_CATEGORY_REGEX); }); it("keeps the real partner catalog routable", () => { @@ -1001,9 +1049,7 @@ describe("mergeToolkitAndPartnerInfo", () => { [{ ...searchToolkit, slug: "tavily" }], [searchPartner] ) - ).toThrow( - /Partner "Tavily" and toolkit "GoogleSearch" both resolve to search\/tavily/ - ); + ).toThrow(PARTNER_SLUG_COLLISION_REGEX); }); it("allows the same slug in different categories", () => { @@ -1048,13 +1094,20 @@ describe("generateCategoryMeta partner section", () => { "/en/resources/integrations" ); - expect(result).toContain('"-- Partners"'); - expect(result).toContain("tavily: {"); - expect(result).toContain( - 'href: "/en/resources/integrations/search/tavily"' + const { pages } = parseMeta(result); + expect(pages).toContain("---Partners---"); + // Partner pages are authored MDX files in the category folder, so they + // are listed by slug (Fumadocs reads the title from the page), not as a + // link entry. + expect(pages).toContain("tavily"); + expect(pages).not.toContain( + "[Tavily](/en/resources/integrations/search/tavily)" + ); + expect(pages.indexOf("---Partners---")).toBeGreaterThan( + pages.indexOf("---Starter---") ); - expect(result.indexOf('"-- Partners"')).toBeGreaterThan( - result.indexOf('"-- Starter"') + expect(pages.indexOf("tavily")).toBeGreaterThan( + pages.indexOf("---Partners---") ); }); @@ -1065,8 +1118,9 @@ describe("generateCategoryMeta partner section", () => { "/en/resources/integrations" ); - expect(result).not.toContain('"-- Partners"'); - expect(result).not.toContain("tavily: {"); + const { pages } = parseMeta(result); + expect(pages).not.toContain("---Partners---"); + expect(pages).not.toContain("tavily"); }); it("renders a partner-only category without duplicating entries", () => { @@ -1076,7 +1130,7 @@ describe("generateCategoryMeta partner section", () => { "/en/resources/integrations" ); - expect(result).toContain('"-- Partners"'); - expect(result.match(/tavily: \{/g)).toHaveLength(1); + const { pages } = parseMeta(result); + expect(pages).toEqual(["---Partners---", "tavily"]); }); }); diff --git a/toolkit-docs-generator/tests/scripts/toolkit-route.test.ts b/toolkit-docs-generator/tests/scripts/toolkit-route.test.ts new file mode 100644 index 000000000..99578e823 --- /dev/null +++ b/toolkit-docs-generator/tests/scripts/toolkit-route.test.ts @@ -0,0 +1,108 @@ +/** + * Smoke test for lib/toolkit-route.tsx, the catch-all route's fallback for + * toolkit pages rendered from toolkit-docs-generator/data/toolkits. + * + * Run with: bunx vitest run toolkit-docs-generator/tests/scripts/toolkit-route.test.ts + */ + +import { describe, expect, it, vi } from "vitest"; + +// The route module renders the client-only toolkit UI (design-system +// components need a DOM) inside Fumadocs page chrome. The pure routing, +// param and metadata logic under test does not, so stub both boundaries. +vi.mock("../../../components/toolkit-docs", () => ({ + ToolkitPage: () => null, +})); +vi.mock("fumadocs-ui/layouts/docs/page", () => ({ + DocsPage: () => null, +})); +// The Fumadocs source imports compiled MDX, which Vitest cannot load without +// the fumadocs-mdx plugin; the route only asks it whether an MDX page already +// owns a slug, and no authored page collides with a generated toolkit. +vi.mock("../../../lib/source", () => ({ + source: { getPage: vi.fn() }, +})); + +import { + generateToolkitParams, + matchToolkitRoute, + toolkitRouteMetadata, +} from "../../../lib/toolkit-route"; +import { listToolkitRoutes } from "../../../lib/toolkit-static-params"; +import { INTEGRATION_CATEGORIES } from "../../src/shared/toolkit-primitives"; + +describe("matchToolkitRoute", () => { + it("matches resources/integrations/<category>/<toolkitId>", () => { + expect( + matchToolkitRoute(["resources", "integrations", "productivity", "gmail"]) + ).toEqual({ category: "productivity", toolkitId: "gmail" }); + }); + + it("rejects other slugs", () => { + expect(matchToolkitRoute(undefined)).toBeNull(); + expect(matchToolkitRoute([])).toBeNull(); + expect(matchToolkitRoute(["resources", "integrations"])).toBeNull(); + expect( + matchToolkitRoute(["resources", "integrations", "productivity"]) + ).toBeNull(); + expect( + matchToolkitRoute(["guides", "integrations", "productivity", "gmail"]) + ).toBeNull(); + expect( + matchToolkitRoute(["resources", "integrations", "a", "b", "c"]) + ).toBeNull(); + }); +}); + +describe("generateToolkitParams", () => { + it("emits one four-segment slug per generated toolkit route", async () => { + const [params, routes] = await Promise.all([ + generateToolkitParams(), + listToolkitRoutes(), + ]); + + expect(routes.length).toBeGreaterThan(0); + expect(params).toHaveLength(routes.length); + for (const { slug } of params) { + expect(slug).toHaveLength(4); + expect(slug.slice(0, 2)).toEqual(["resources", "integrations"]); + expect(INTEGRATION_CATEGORIES).toContain(slug[2]); + } + // Partner pages are authored MDX, never generated params. + expect(params.map(({ slug }) => slug.join("/"))).not.toContain( + "resources/integrations/search/tavily" + ); + }); +}); + +describe("toolkitRouteMetadata", () => { + it("returns null for an unknown toolkit", async () => { + expect( + await toolkitRouteMetadata({ + category: "productivity", + toolkitId: "does-not-exist", + }) + ).toBeNull(); + }); + + it("canonicalizes to the toolkit's own category", async () => { + const [route] = await listToolkitRoutes(); + if (!route) { + throw new Error("Expected at least one generated toolkit route"); + } + const wrongCategory = INTEGRATION_CATEGORIES.find( + (category) => category !== route.category + ); + + const metadata = await toolkitRouteMetadata({ + category: wrongCategory ?? route.category, + toolkitId: route.toolkitId, + }); + + expect(metadata).not.toBeNull(); + expect(metadata?.title).toBeTruthy(); + expect(metadata?.alternates?.canonical).toBe( + `/en/resources/integrations/${route.category}/${route.toolkitId}` + ); + }); +}); diff --git a/toolkit-docs-generator/tests/sources/markdown-curation.test.ts b/toolkit-docs-generator/tests/sources/markdown-curation.test.ts index 25dfb6052..ea721b3c6 100644 --- a/toolkit-docs-generator/tests/sources/markdown-curation.test.ts +++ b/toolkit-docs-generator/tests/sources/markdown-curation.test.ts @@ -119,7 +119,7 @@ type: environment-variables `--- type: import --- -import StarterToolInfo from "@/app/_components/starter-tool-info"; +import StarterToolInfo from "@/components/starter-tool-info"; ` ); @@ -127,7 +127,7 @@ import StarterToolInfo from "@/app/_components/starter-tool-info"; await createMarkdownCurationSource(tempDir).getCustomSections("Github"); expect(sections.customImports).toEqual([ - 'import StarterToolInfo from "@/app/_components/starter-tool-info";', + 'import StarterToolInfo from "@/components/starter-tool-info";', ]); }); diff --git a/toolkit-docs-generator/tests/sources/public-catalog-api.test.ts b/toolkit-docs-generator/tests/sources/public-catalog-api.test.ts index 8775472ca..edf295141 100644 --- a/toolkit-docs-generator/tests/sources/public-catalog-api.test.ts +++ b/toolkit-docs-generator/tests/sources/public-catalog-api.test.ts @@ -165,7 +165,9 @@ describe("fetchAllPages", () => { it("includes the response body, capped at 500 characters, when the API rejects a page", async () => { const fetchFn = (async () => - new Response("x".repeat(2000), { status: 400 })) as typeof fetch; + new Response("x".repeat(2000), { + status: 400, + })) as unknown as typeof fetch; await expect( fetchAllPages("https://api.example/v1/public/tools", fetchFn) diff --git a/toolkit-docs-generator/tests/workflows/generate-toolkit-docs.test.ts b/toolkit-docs-generator/tests/workflows/generate-toolkit-docs.test.ts index c33dcd69f..0e32b8a97 100644 --- a/toolkit-docs-generator/tests/workflows/generate-toolkit-docs.test.ts +++ b/toolkit-docs-generator/tests/workflows/generate-toolkit-docs.test.ts @@ -10,6 +10,8 @@ const workflowPath = join( ); const workflowContents = readFileSync(workflowPath, "utf-8"); +const GENERATOR_BUILD_STEP_REGEX = + /run: bun run build\s*\n\s*working-directory: toolkit-docs-generator/; test("porter workflow includes required triggers", () => { expect(workflowContents).toContain("repository_dispatch"); @@ -23,9 +25,12 @@ test("porter workflow generates docs and opens a PR", () => { expect(workflowContents).toContain( "../node_modules/.bin/tsx src/cli/index.ts generate" ); - // pnpm dlx resolves an unpinned tsx from the registry on every run, so the - // nightly's TypeScript runtime would drift outside the lockfile. + // The workflow calls the tsx pinned in the lockfile. `pnpm dlx` / `bunx tsx` + // could resolve an unpinned tsx from the registry, so the nightly's + // TypeScript runtime would drift outside the lockfile. expect(workflowContents).not.toContain("pnpm dlx"); + expect(workflowContents).not.toContain("bunx tsx"); + expect(workflowContents).not.toContain("pnpm"); expect(workflowContents).toContain("--skip-unchanged"); expect(workflowContents).toContain("--preserve-last-known-good"); expect(workflowContents).toContain("--verbose"); @@ -50,20 +55,18 @@ test("porter workflow generates docs and opens a PR", () => { expect(workflowContents).toContain("pull-requests: write"); // GITHUB_TOKEN PRs do not start other workflows. Generate llms.txt here // instead of depending on llmstxt.yml, and open the PR with github.token. - expect(workflowContents).toContain("pnpm llmstxt"); + expect(workflowContents).toContain("bun run llmstxt"); expect(workflowContents).toContain(`token: ${"${"}{ github.token }}`); expect(workflowContents).not.toContain("uses: ./.github/actions/app-token"); expect(workflowContents).not.toContain("DOCS_BOT"); }); test("porter workflow does not build the docs generator before running it", () => { - // toolkit-docs-generator has no package.json, so a `pnpm build` step - // there resolves to the root manifest's `next build --webpack` — a full - // Next.js production build that the tsx-executed CLI below doesn't need. + // toolkit-docs-generator has no package.json, so a `bun run build` step + // there resolves to the root manifest's `next build` — a full Next.js + // production build that the bun-executed CLI below doesn't need. expect(workflowContents).not.toContain("Build toolkit docs generator"); - expect(workflowContents).not.toMatch( - /run: pnpm build\s*\n\s*working-directory: toolkit-docs-generator/ - ); + expect(workflowContents).not.toMatch(GENERATOR_BUILD_STEP_REGEX); }); test("porter workflow wires the secret-coherence editor", () => { diff --git a/toolkit-docs-generator/tsconfig.json b/toolkit-docs-generator/tsconfig.json index 1d47aaddd..661b9573f 100644 --- a/toolkit-docs-generator/tsconfig.json +++ b/toolkit-docs-generator/tsconfig.json @@ -21,6 +21,7 @@ "exclude": [ "node_modules", "dist", - "tests/scripts/sync-toolkit-sidebar.test.ts" + "tests/scripts/sync-toolkit-sidebar.test.ts", + "tests/scripts/toolkit-route.test.ts" ] } diff --git a/tsconfig.json b/tsconfig.json index 7ccc681bd..d016fae24 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ES2017", + "target": "ES2022", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, @@ -19,33 +19,17 @@ } ], "paths": { - "@/*": ["./*"] - }, - "strictNullChecks": true + "@/*": ["./*"], + "@/.source/*": ["./.source/*"] + } }, - "files": [ - "toolkit-docs-generator/tests/scripts/sync-toolkit-sidebar.test.ts" - ], "include": [ "next-env.d.ts", - "app/**/*.ts", - "app/**/*.tsx", - "lib/**/*.ts", - "tests/**/*.ts", - "tests/**/*.tsx", - "_dictionaries/**/*.ts", + "**/*.ts", + "**/*.tsx", + ".source/**/*.ts", ".next/types/**/*.ts", - "app/[lang]/page.mdx", - "mdx-components.ts", - "app/en/home/page.mdx", - ".next/dev/types/**/*.ts", - "app/en/references/page.mdx", - "config.ts" + ".next/dev/types/**/*.ts" ], - "exclude": [ - "node_modules", - "extract-custom-sections", - "toolkit-docs-generator", - "toolkit-docs-generator-verification" - ] + "exclude": ["node_modules", "scripts", "toolkit-docs-generator", ".next"] }