diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..5334e79 --- /dev/null +++ b/.env.example @@ -0,0 +1,8 @@ +# AI Chat Feature - Get your free API keys: + +# Upstash Vector (https://console.upstash.com/vector) +UPSTASH_VECTOR_REST_URL= +UPSTASH_VECTOR_REST_TOKEN= + +# Google Gemini (https://aistudio.google.com/apikey) +GOOGLE_GENERATIVE_AI_API_KEY= diff --git a/.gitignore b/.gitignore index 3362b7e..73327c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,13 @@ # build output dist/ +template/dist/ # generated types .astro/ # dependencies node_modules/ +template/node_modules/ # logs npm-debug.log* @@ -54,6 +56,7 @@ coverage/ # Cache .cache/ *.tsbuildinfo +.index-manifest.json # Temporary files tmp/ diff --git a/CHANGELOG.md b/CHANGELOG.md index ccf75a0..0f16b98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # create-docubase +## 1.2.0 + +### Minor Changes + +- 8cceec4: Add AI chat assistant with RAG-powered documentation search + - Add AskAnything component for homepage chat input + - Add ChatWidget floating chat button component + - Add dedicated chat page with conversation history + - Add chat API endpoint with Google Gemini integration + - Add Upstash Vector for semantic search + - Add index-content scripts for development and production + - Fix: XSS protection for AI-generated markdown links + - Fix: Proper SSE format for streaming responses + - Fix: Initialization guards for event listeners + +- 8cceec4: AI chat assistant with RAG-powered documentation search + ## 1.1.2 ### Patch Changes diff --git a/README.md b/README.md index e529726..dd26831 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Your site will be available at `http://localhost:4321`. - **Content-First**: Write documentation in MDX with full component support - **Framework-Agnostic**: Works with any UI library or none at all - **Multiple Content Types**: Documentation, blog posts, and tutorials out of the box +- **AI Chat Assistant**: Built-in RAG-powered chat that answers questions about your docs - **Static Site Generation**: Fast, SEO-friendly pages that deploy anywhere - **Type-Safe**: Full TypeScript support with Astro Content Collections - **Dark Mode**: Automatic dark mode support via CSS media queries @@ -31,6 +32,8 @@ Your site will be available at `http://localhost:4321`. - [MDX](https://mdxjs.com/) - Markdown with components - [TypeScript](https://www.typescriptlang.org/) - Type safety - [Tailwind CSS](https://tailwindcss.com/) - Styling +- [Google Gemini](https://ai.google.dev/) - AI chat responses +- [Upstash Vector](https://upstash.com/vector) - Vector search for RAG - [Storybook](https://storybook.js.org/) - Component playground (optional) ## Installation @@ -271,6 +274,43 @@ import Example from '../../components/Example.astro'; ``` +## AI Chat Assistant + +DocuBase includes a built-in AI chat assistant that uses RAG (Retrieval-Augmented Generation) to answer questions about your documentation. + +### Setup + +1. Copy the environment file: + +```bash +cp .env.example .env +``` + +2. Add your API keys to `.env`: + +```env +# Upstash Vector (https://console.upstash.com/vector) +UPSTASH_VECTOR_REST_URL=your_url_here +UPSTASH_VECTOR_REST_TOKEN=your_token_here + +# Google Gemini (https://aistudio.google.com/apikey) +GOOGLE_GENERATIVE_AI_API_KEY=your_key_here +``` + +3. That's it! Content is automatically indexed on every build. + +### How It Works + +- Content is automatically indexed when you run `pnpm run build` +- Users can ask questions via the chat widget or "Ask Anything" input +- The AI searches your docs and provides relevant answers with sources +- No manual indexing required - just write docs and deploy + +### Components + +- **AskAnything**: Homepage input for starting a chat +- **ChatWidget**: Floating chat button (can be added to any layout) + ## Storybook (Optional) DocuBase includes an optional Storybook setup for developing and showcasing components: diff --git a/astro.config.mjs b/astro.config.mjs index 529bc68..72aa5e0 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -5,12 +5,14 @@ import { fileURLToPath } from 'node:url'; import tailwindcss from '@tailwindcss/vite'; import mdx from '@astrojs/mdx'; +import vercel from '@astrojs/vercel'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); export default defineConfig({ site: 'https://docubase-docs.vercel.app', output: 'static', + adapter: vercel(), srcDir: './template/src', publicDir: './template/public', vite: { diff --git a/package.json b/package.json index 34f20dc..77d6f18 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "create-docubase", - "version": "1.1.2", + "version": "1.2.0", "description": "Create a new DocuBase documentation site", "type": "module", "bin": { @@ -13,6 +13,7 @@ "scripts": { "dev": "astro dev", "build": "astro build && pagefind --site dist", + "build:with-index": "pnpm run index-content && astro build && pagefind --site dist", "preview": "astro preview", "astro": "astro", "test": "vitest run", @@ -22,7 +23,9 @@ "version": "changeset version", "release": "changeset publish", "storybook": "storybook dev -p 6006", - "build-storybook": "storybook build" + "build-storybook": "storybook build", + "index-content": "npx tsx scripts/index-content.ts", + "index-content:watch": "npx tsx scripts/index-watch.ts" }, "keywords": [ "documentation", @@ -41,9 +44,14 @@ "dependencies": { "@astrojs/check": "^0.9.6", "@astrojs/mdx": "^4.3.13", + "@astrojs/vercel": "^8.0.0", + "@google/generative-ai": "^0.21.0", "@tailwindcss/vite": "^4.1.18", + "@upstash/vector": "^1.1.0", "astro": "^5.16.11", + "dotenv": "^16.4.0", "fs-extra": "^11.2.0", + "gray-matter": "^4.0.3", "iconify-icon": "^3.0.2", "picocolors": "^1.1.1", "prompts": "^2.4.2", @@ -63,8 +71,9 @@ "@types/fs-extra": "^11.0.4", "@types/node": "^25.0.10", "@types/prompts": "^2.4.9", - "storybook": "^8.4.0", "pagefind": "^1.4.0", + "storybook": "^8.4.0", + "tsx": "^4.19.0", "vitest": "^3.0.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index da2bbc7..a5bed2f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,16 +13,31 @@ importers: version: 0.9.6(prettier@3.8.1)(typescript@5.9.3) '@astrojs/mdx': specifier: ^4.3.13 - version: 4.3.13(astro@5.16.13(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.56.0)(typescript@5.9.3)(yaml@2.8.2)) + version: 4.3.13(astro@5.16.13(@types/node@25.0.10)(@vercel/functions@2.2.13)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)) + '@astrojs/vercel': + specifier: ^8.0.0 + version: 8.2.11(astro@5.16.13(@types/node@25.0.10)(@vercel/functions@2.2.13)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(react@19.2.3)(rollup@4.56.0) + '@google/generative-ai': + specifier: ^0.21.0 + version: 0.21.0 '@tailwindcss/vite': specifier: ^4.1.18 - version: 4.1.18(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) + version: 4.1.18(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@upstash/vector': + specifier: ^1.1.0 + version: 1.2.3 astro: specifier: ^5.16.11 - version: 5.16.13(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.56.0)(typescript@5.9.3)(yaml@2.8.2) + version: 5.16.13(@types/node@25.0.10)(@vercel/functions@2.2.13)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) + dotenv: + specifier: ^16.4.0 + version: 16.6.1 fs-extra: specifier: ^11.2.0 version: 11.3.3 + gray-matter: + specifier: ^4.0.3 + version: 4.0.3 iconify-icon: specifier: ^3.0.2 version: 3.0.2 @@ -62,7 +77,7 @@ importers: version: 8.6.15(storybook@8.6.15(prettier@3.8.1)) '@storybook/html-vite': specifier: ^8.4.0 - version: 8.6.15(storybook@8.6.15(prettier@3.8.1))(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) + version: 8.6.15(storybook@8.6.15(prettier@3.8.1))(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) '@storybook/test': specifier: ^8.4.0 version: 8.6.15(storybook@8.6.15(prettier@3.8.1)) @@ -81,9 +96,12 @@ importers: storybook: specifier: ^8.4.0 version: 8.6.15(prettier@3.8.1) + tsx: + specifier: ^4.19.0 + version: 4.21.0 vitest: specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) packages: @@ -99,6 +117,9 @@ packages: '@astrojs/compiler@2.13.0': resolution: {integrity: sha512-mqVORhUJViA28fwHYaWmsXSzLO9osbdZ5ImUfxBarqsYdMlPbqAqGJCxsNzvppp1BEzc1mJNjOVvQqeDN8Vspw==} + '@astrojs/internal-helpers@0.7.4': + resolution: {integrity: sha512-lDA9MqE8WGi7T/t2BMi+EAXhs4Vcvr94Gqx3q15cFEz8oFZMO4/SFBqYr/UcmNlvW+35alowkVj+w9VhLvs5Cw==} + '@astrojs/internal-helpers@0.7.5': resolution: {integrity: sha512-vreGnYSSKhAjFJCWAwe/CNhONvoc5lokxtRoZims+0wa3KbHBdPHSSthJsKxPd8d/aic6lWKpRTYGY/hsgK6EA==} @@ -131,6 +152,11 @@ packages: resolution: {integrity: sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==} engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + '@astrojs/vercel@8.2.11': + resolution: {integrity: sha512-PGtWHvHYMkT8ftSR3yuR7oyf/oPvOv8AfhCFlSQg318hfpalSEPND9mjbdQGpMeZz3KtvvOnHyYwqmu5V8MSHg==} + peerDependencies: + astro: ^5.0.0 + '@astrojs/yaml2ts@0.2.2': resolution: {integrity: sha512-GOfvSr5Nqy2z5XiwqTouBBpy5FyI6DEe+/g/Mk5am9SjILN1S5fOEvYK0GuWHg98yS/dobP4m8qyqw/URW35fQ==} @@ -248,156 +274,316 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.27.4': + resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.25.12': resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} engines: {node: '>=18'} cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.27.4': + resolution: {integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.25.12': resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} engines: {node: '>=18'} cpu: [arm] os: [android] + '@esbuild/android-arm@0.27.4': + resolution: {integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.25.12': resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} engines: {node: '>=18'} cpu: [x64] os: [android] + '@esbuild/android-x64@0.27.4': + resolution: {integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.25.12': resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.27.4': + resolution: {integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.25.12': resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.27.4': + resolution: {integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.25.12': resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.27.4': + resolution: {integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.25.12': resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.27.4': + resolution: {integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.25.12': resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.27.4': + resolution: {integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.25.12': resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} engines: {node: '>=18'} cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.27.4': + resolution: {integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.25.12': resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} engines: {node: '>=18'} cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.27.4': + resolution: {integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.25.12': resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} engines: {node: '>=18'} cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.27.4': + resolution: {integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.25.12': resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.27.4': + resolution: {integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.25.12': resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.27.4': + resolution: {integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.25.12': resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.27.4': + resolution: {integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.25.12': resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} engines: {node: '>=18'} cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.27.4': + resolution: {integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.25.12': resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.27.4': + resolution: {integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.25.12': resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.27.4': + resolution: {integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.25.12': resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.27.4': + resolution: {integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.25.12': resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.27.4': + resolution: {integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.25.12': resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.27.4': + resolution: {integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openharmony-arm64@0.25.12': resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] + '@esbuild/openharmony-arm64@0.27.4': + resolution: {integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.25.12': resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} engines: {node: '>=18'} cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.27.4': + resolution: {integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.25.12': resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.27.4': + resolution: {integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.25.12': resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.27.4': + resolution: {integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.25.12': resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} engines: {node: '>=18'} cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.27.4': + resolution: {integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@google/generative-ai@0.21.0': + resolution: {integrity: sha512-7XhUbtnlkSEZK15kN3t+tzIMxsbKm/dSkKBFalj+20NvPKe1kBY7mR2P7vuijEn+f06z5+A8bVGKO0v39cr6Wg==} + engines: {node: '>=18.0.0'} + '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} @@ -547,6 +733,14 @@ packages: '@types/node': optional: true + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -569,6 +763,11 @@ packages: '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + '@mapbox/node-pre-gyp@2.0.3': + resolution: {integrity: sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==} + engines: {node: '>=18'} + hasBin: true + '@mdx-js/mdx@3.1.1': resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} @@ -623,6 +822,10 @@ packages: cpu: [x64] os: [win32] + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + '@rollup/pluginutils@5.3.0': resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} engines: {node: '>=14.0.0'} @@ -1114,6 +1317,56 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@upstash/vector@1.2.3': + resolution: {integrity: sha512-yXsWKeuHNYyH72BcSZd3bV5ZD5MybAoTvKxkMaeV2UzuGfNzbHBVh5eO+ysTWTFAf8I9XcOueF4tZfAGjCa4Iw==} + + '@vercel/analytics@1.6.1': + resolution: {integrity: sha512-oH9He/bEM+6oKlv3chWuOOcp8Y6fo6/PSro8hEkgCW3pu9/OiCXiUpRUogDh3Fs3LH2sosDrx8CxeOLBEE+afg==} + peerDependencies: + '@remix-run/react': ^2 + '@sveltejs/kit': ^1 || ^2 + next: '>= 13' + react: ^18 || ^19 || ^19.0.0-rc + svelte: '>= 4' + vue: ^3 + vue-router: ^4 + peerDependenciesMeta: + '@remix-run/react': + optional: true + '@sveltejs/kit': + optional: true + next: + optional: true + react: + optional: true + svelte: + optional: true + vue: + optional: true + vue-router: + optional: true + + '@vercel/functions@2.2.13': + resolution: {integrity: sha512-14ArBSIIcOBx9nrEgaJb4Bw+en1gl6eSoJWh8qjifLl5G3E4dRXCFOT8HP+w66vb9Wqyd1lAQBrmRhRwOj9X9A==} + engines: {node: '>= 18'} + peerDependencies: + '@aws-sdk/credential-provider-web-identity': '*' + peerDependenciesMeta: + '@aws-sdk/credential-provider-web-identity': + optional: true + + '@vercel/nft@0.30.3': + resolution: {integrity: sha512-UEq+eF0ocEf9WQCV1gktxKhha36KDs7jln5qii6UpPf5clMqDc0p3E7d9l2Smx0i9Pm1qpq4S4lLfNl97bbv6w==} + engines: {node: '>=18'} + hasBin: true + + '@vercel/oidc@2.0.2': + resolution: {integrity: sha512-59PBFx3T+k5hLTEWa3ggiMpGRz1OVvl9eN8SUai+A43IsqiOuAe7qPBf+cray/Fj6mkgnxm/D7IAtjc8zSHi7g==} + engines: {node: '>= 18'} + + '@vercel/routing-utils@5.3.3': + resolution: {integrity: sha512-KYm2sLNUD48gDScv8ob4ejc3Gww2jcJyW80hTdYlenAPz/5BQar1Gyh38xrUuZ532TUwSb5mV1uRbAuiykq0EQ==} + '@vitest/expect@2.0.5': resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==} @@ -1187,6 +1440,15 @@ packages: '@vscode/l10n@0.0.18': resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==} + abbrev@3.0.1: + resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} + engines: {node: ^18.17.0 || >=20.5.0} + + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1197,6 +1459,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + ajv-draft-04@1.0.0: resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} peerDependencies: @@ -1205,6 +1471,9 @@ packages: ajv: optional: true + ajv@6.14.0: + resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} @@ -1276,6 +1545,9 @@ packages: engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'} hasBin: true + async-sema@3.1.1: + resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==} + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -1287,6 +1559,9 @@ packages: bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base-64@1.0.0: resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} @@ -1298,6 +1573,9 @@ packages: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -1305,6 +1583,9 @@ packages: resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} engines: {node: '>=18'} + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -1378,6 +1659,10 @@ packages: resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} engines: {node: '>= 20.19.0'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + ci-info@3.9.0: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} @@ -1418,6 +1703,10 @@ packages: common-ancestor-path@1.0.1: resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + cookie-es@1.2.2: resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} @@ -1544,6 +1833,10 @@ packages: domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + dset@3.1.4: resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} engines: {node: '>=4'} @@ -1552,6 +1845,9 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + emmet@2.4.11: resolution: {integrity: sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==} @@ -1561,6 +1857,9 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + enhanced-resolve@5.18.4: resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} engines: {node: '>=10.13.0'} @@ -1608,6 +1907,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.27.4: + resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -1652,6 +1956,10 @@ packages: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -1665,6 +1973,9 @@ packages: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} @@ -1680,6 +1991,9 @@ packages: picomatch: optional: true + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -1703,6 +2017,10 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + fs-extra@11.3.3: resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==} engines: {node: '>=14.14'} @@ -1743,6 +2061,9 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + get-tsconfig@4.13.7: + resolution: {integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==} + github-slugger@2.0.0: resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} @@ -1750,6 +2071,11 @@ packages: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -1761,6 +2087,10 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + gray-matter@4.0.3: + resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} + engines: {node: '>=6.0'} + h3@1.15.5: resolution: {integrity: sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg==} @@ -1834,6 +2164,10 @@ packages: http-cache-semantics@4.2.0: resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + human-id@4.1.3: resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==} hasBin: true @@ -1892,6 +2226,10 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} hasBin: true + is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -1951,6 +2289,9 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true @@ -1973,6 +2314,9 @@ packages: resolution: {integrity: sha512-iZ8Bdb84lWRuGHamRXFyML07r21pcwBrLkHEuHgEY5UbCouBwv7ECknDRKzsQIXMiqpPymqtIf8TC/shYKB5rw==} engines: {node: '>=12.0.0'} + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} @@ -1988,6 +2332,10 @@ packages: jsonfile@6.2.0: resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} @@ -2085,6 +2433,9 @@ packages: loupe@3.2.1: resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.2.4: resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} engines: {node: 20 || >=22} @@ -2290,6 +2641,18 @@ packages: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -2319,9 +2682,27 @@ packages: node-fetch-native@1.6.7: resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + node-mock-http@1.0.4: resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==} + nopt@8.1.0: + resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -2380,6 +2761,9 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + package-manager-detector@0.2.11: resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} @@ -2410,6 +2794,16 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-to-regexp@6.1.0: + resolution: {integrity: sha512-h9DqehX3zZZDCEm+xbfU0ZmwCGFCAAraPJWMXJ4+v32NjZJilVg3k1TcKsRgIb8IQ/izZSaydDc1OhJCZvs2Dw==} + + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -2480,6 +2874,10 @@ packages: property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} @@ -2599,6 +2997,9 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + retext-latin@4.0.0: resolution: {integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==} @@ -2637,6 +3038,10 @@ packages: scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + section-matter@1.0.0: + resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} + engines: {node: '>=4'} + semver@7.7.3: resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} @@ -2719,6 +3124,10 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + string-width@7.2.0: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} @@ -2734,6 +3143,10 @@ packages: resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} + strip-bom-string@1.0.0: + resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} + engines: {node: '>=0.10.0'} + strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} @@ -2767,6 +3180,10 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} + tar@7.5.13: + resolution: {integrity: sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==} + engines: {node: '>=18'} + term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} @@ -2815,6 +3232,9 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -2838,6 +3258,11 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + type-fest@4.41.0: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} @@ -2975,6 +3400,9 @@ packages: uploadthing: optional: true + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + util@0.12.5: resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} @@ -3167,9 +3595,15 @@ packages: web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webpack-virtual-modules@0.6.2: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + which-pm-runs@1.1.0: resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} engines: {node: '>=4'} @@ -3196,6 +3630,10 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + wrap-ansi@9.0.2: resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} engines: {node: '>=18'} @@ -3219,6 +3657,10 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yaml-language-server@1.19.2: resolution: {integrity: sha512-9F3myNmJzUN/679jycdMxqtydPSDRAarSj3wPiF7pchEPnO9Dg07Oc+gIYLqXR4L+g+FSEVXXv2+mr54StLFOg==} hasBin: true @@ -3287,6 +3729,8 @@ snapshots: '@astrojs/compiler@2.13.0': {} + '@astrojs/internal-helpers@0.7.4': {} + '@astrojs/internal-helpers@0.7.5': {} '@astrojs/language-server@2.16.3(prettier@3.8.1)(typescript@5.9.3)': @@ -3340,12 +3784,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@astrojs/mdx@4.3.13(astro@5.16.13(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.56.0)(typescript@5.9.3)(yaml@2.8.2))': + '@astrojs/mdx@4.3.13(astro@5.16.13(@types/node@25.0.10)(@vercel/functions@2.2.13)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))': dependencies: '@astrojs/markdown-remark': 6.3.10 '@mdx-js/mdx': 3.1.1 acorn: 8.15.0 - astro: 5.16.13(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.56.0)(typescript@5.9.3)(yaml@2.8.2) + astro: 5.16.13(@types/node@25.0.10)(@vercel/functions@2.2.13)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) es-module-lexer: 1.7.0 estree-util-visit: 2.0.0 hast-util-to-html: 9.0.5 @@ -3375,6 +3819,29 @@ snapshots: transitivePeerDependencies: - supports-color + '@astrojs/vercel@8.2.11(astro@5.16.13(@types/node@25.0.10)(@vercel/functions@2.2.13)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(react@19.2.3)(rollup@4.56.0)': + dependencies: + '@astrojs/internal-helpers': 0.7.4 + '@vercel/analytics': 1.6.1(react@19.2.3) + '@vercel/functions': 2.2.13 + '@vercel/nft': 0.30.3(rollup@4.56.0) + '@vercel/routing-utils': 5.3.3 + astro: 5.16.13(@types/node@25.0.10)(@vercel/functions@2.2.13)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) + esbuild: 0.25.12 + tinyglobby: 0.2.15 + transitivePeerDependencies: + - '@aws-sdk/credential-provider-web-identity' + - '@remix-run/react' + - '@sveltejs/kit' + - encoding + - next + - react + - rollup + - supports-color + - svelte + - vue + - vue-router + '@astrojs/yaml2ts@0.2.2': dependencies: yaml: 2.8.2 @@ -3579,81 +4046,161 @@ snapshots: '@esbuild/aix-ppc64@0.25.12': optional: true + '@esbuild/aix-ppc64@0.27.4': + optional: true + '@esbuild/android-arm64@0.25.12': optional: true + '@esbuild/android-arm64@0.27.4': + optional: true + '@esbuild/android-arm@0.25.12': optional: true + '@esbuild/android-arm@0.27.4': + optional: true + '@esbuild/android-x64@0.25.12': optional: true + '@esbuild/android-x64@0.27.4': + optional: true + '@esbuild/darwin-arm64@0.25.12': optional: true + '@esbuild/darwin-arm64@0.27.4': + optional: true + '@esbuild/darwin-x64@0.25.12': optional: true + '@esbuild/darwin-x64@0.27.4': + optional: true + '@esbuild/freebsd-arm64@0.25.12': optional: true + '@esbuild/freebsd-arm64@0.27.4': + optional: true + '@esbuild/freebsd-x64@0.25.12': optional: true + '@esbuild/freebsd-x64@0.27.4': + optional: true + '@esbuild/linux-arm64@0.25.12': optional: true + '@esbuild/linux-arm64@0.27.4': + optional: true + '@esbuild/linux-arm@0.25.12': optional: true + '@esbuild/linux-arm@0.27.4': + optional: true + '@esbuild/linux-ia32@0.25.12': optional: true + '@esbuild/linux-ia32@0.27.4': + optional: true + '@esbuild/linux-loong64@0.25.12': optional: true + '@esbuild/linux-loong64@0.27.4': + optional: true + '@esbuild/linux-mips64el@0.25.12': optional: true + '@esbuild/linux-mips64el@0.27.4': + optional: true + '@esbuild/linux-ppc64@0.25.12': optional: true + '@esbuild/linux-ppc64@0.27.4': + optional: true + '@esbuild/linux-riscv64@0.25.12': optional: true + '@esbuild/linux-riscv64@0.27.4': + optional: true + '@esbuild/linux-s390x@0.25.12': optional: true + '@esbuild/linux-s390x@0.27.4': + optional: true + '@esbuild/linux-x64@0.25.12': optional: true + '@esbuild/linux-x64@0.27.4': + optional: true + '@esbuild/netbsd-arm64@0.25.12': optional: true + '@esbuild/netbsd-arm64@0.27.4': + optional: true + '@esbuild/netbsd-x64@0.25.12': optional: true + '@esbuild/netbsd-x64@0.27.4': + optional: true + '@esbuild/openbsd-arm64@0.25.12': optional: true + '@esbuild/openbsd-arm64@0.27.4': + optional: true + '@esbuild/openbsd-x64@0.25.12': optional: true + '@esbuild/openbsd-x64@0.27.4': + optional: true + '@esbuild/openharmony-arm64@0.25.12': optional: true + '@esbuild/openharmony-arm64@0.27.4': + optional: true + '@esbuild/sunos-x64@0.25.12': optional: true + '@esbuild/sunos-x64@0.27.4': + optional: true + '@esbuild/win32-arm64@0.25.12': optional: true + '@esbuild/win32-arm64@0.27.4': + optional: true + '@esbuild/win32-ia32@0.25.12': optional: true + '@esbuild/win32-ia32@0.27.4': + optional: true + '@esbuild/win32-x64@0.25.12': optional: true + '@esbuild/win32-x64@0.27.4': + optional: true + + '@google/generative-ai@0.21.0': {} + '@iconify/types@2.0.0': {} '@img/colour@1.0.0': @@ -3760,6 +4307,19 @@ snapshots: optionalDependencies: '@types/node': 25.0.10 + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.2 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.3 + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -3795,6 +4355,19 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 + '@mapbox/node-pre-gyp@2.0.3': + dependencies: + consola: 3.4.2 + detect-libc: 2.1.2 + https-proxy-agent: 7.0.6 + node-fetch: 2.7.0 + nopt: 8.1.0 + semver: 7.7.3 + tar: 7.5.13 + transitivePeerDependencies: + - encoding + - supports-color + '@mdx-js/mdx@3.1.1': dependencies: '@types/estree': 1.0.8 @@ -3863,6 +4436,9 @@ snapshots: '@pagefind/windows-x64@1.4.0': optional: true + '@pkgjs/parseargs@0.11.0': + optional: true + '@rollup/pluginutils@5.3.0(rollup@4.56.0)': dependencies: '@types/estree': 1.0.8 @@ -4083,13 +4659,13 @@ snapshots: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - '@storybook/builder-vite@8.6.15(storybook@8.6.15(prettier@3.8.1))(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))': + '@storybook/builder-vite@8.6.15(storybook@8.6.15(prettier@3.8.1))(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@storybook/csf-plugin': 8.6.15(storybook@8.6.15(prettier@3.8.1)) browser-assert: 1.2.1 storybook: 8.6.15(prettier@3.8.1) ts-dedent: 2.2.0 - vite: 6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) '@storybook/components@8.6.15(storybook@8.6.15(prettier@3.8.1))': dependencies: @@ -4128,9 +4704,9 @@ snapshots: '@storybook/global@5.0.0': {} - '@storybook/html-vite@8.6.15(storybook@8.6.15(prettier@3.8.1))(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))': + '@storybook/html-vite@8.6.15(storybook@8.6.15(prettier@3.8.1))(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': dependencies: - '@storybook/builder-vite': 8.6.15(storybook@8.6.15(prettier@3.8.1))(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) + '@storybook/builder-vite': 8.6.15(storybook@8.6.15(prettier@3.8.1))(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) '@storybook/html': 8.6.15(storybook@8.6.15(prettier@3.8.1)) magic-string: 0.30.21 storybook: 8.6.15(prettier@3.8.1) @@ -4265,12 +4841,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18 '@tailwindcss/oxide-win32-x64-msvc': 4.1.18 - '@tailwindcss/vite@4.1.18(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))': + '@tailwindcss/vite@4.1.18(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@tailwindcss/node': 4.1.18 '@tailwindcss/oxide': 4.1.18 tailwindcss: 4.1.18 - vite: 6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) '@testing-library/dom@10.4.0': dependencies: @@ -4364,6 +4940,47 @@ snapshots: '@ungap/structured-clone@1.3.0': {} + '@upstash/vector@1.2.3': {} + + '@vercel/analytics@1.6.1(react@19.2.3)': + optionalDependencies: + react: 19.2.3 + + '@vercel/functions@2.2.13': + dependencies: + '@vercel/oidc': 2.0.2 + + '@vercel/nft@0.30.3(rollup@4.56.0)': + dependencies: + '@mapbox/node-pre-gyp': 2.0.3 + '@rollup/pluginutils': 5.3.0(rollup@4.56.0) + acorn: 8.15.0 + acorn-import-attributes: 1.9.5(acorn@8.15.0) + async-sema: 3.1.1 + bindings: 1.5.0 + estree-walker: 2.0.2 + glob: 10.5.0 + graceful-fs: 4.2.11 + node-gyp-build: 4.8.4 + picomatch: 4.0.3 + resolve-from: 5.0.0 + transitivePeerDependencies: + - encoding + - rollup + - supports-color + + '@vercel/oidc@2.0.2': + dependencies: + '@types/ms': 2.1.0 + ms: 2.1.3 + + '@vercel/routing-utils@5.3.3': + dependencies: + path-to-regexp: 6.1.0 + path-to-regexp-updated: path-to-regexp@6.3.0 + optionalDependencies: + ajv: 6.14.0 + '@vitest/expect@2.0.5': dependencies: '@vitest/spy': 2.0.5 @@ -4379,13 +4996,13 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))': + '@vitest/mocker@3.2.4(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) '@vitest/pretty-format@2.0.5': dependencies: @@ -4488,16 +5105,32 @@ snapshots: '@vscode/l10n@0.0.18': {} + abbrev@3.0.1: {} + + acorn-import-attributes@1.9.5(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 acorn@8.15.0: {} + agent-base@7.1.4: {} + ajv-draft-04@1.0.0(ajv@8.17.1): optionalDependencies: ajv: 8.17.1 + ajv@6.14.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + optional: true + ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 @@ -4552,7 +5185,7 @@ snapshots: astring@1.9.0: {} - astro@5.16.13(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.56.0)(typescript@5.9.3)(yaml@2.8.2): + astro@5.16.13(@types/node@25.0.10)(@vercel/functions@2.2.13)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2): dependencies: '@astrojs/compiler': 2.13.0 '@astrojs/internal-helpers': 0.7.5 @@ -4607,10 +5240,10 @@ snapshots: ultrahtml: 1.6.0 unifont: 0.7.3 unist-util-visit: 5.0.0 - unstorage: 1.17.4 + unstorage: 1.17.4(@vercel/functions@2.2.13) vfile: 6.0.3 - vite: 6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) - vitefu: 1.1.1(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) + vite: 6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vitefu: 1.1.1(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) xxhash-wasm: 1.1.0 yargs-parser: 21.1.1 yocto-spinner: 0.2.3 @@ -4654,6 +5287,8 @@ snapshots: - uploadthing - yaml + async-sema@3.1.1: {} + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 @@ -4662,6 +5297,8 @@ snapshots: bail@2.0.2: {} + balanced-match@1.0.2: {} + base-64@1.0.0: {} better-opn@3.0.2: @@ -4672,6 +5309,10 @@ snapshots: dependencies: is-windows: 1.0.2 + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + boolbase@1.0.0: {} boxen@8.0.1: @@ -4685,6 +5326,10 @@ snapshots: widest-line: 5.0.0 wrap-ansi: 9.0.2 + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -4754,6 +5399,8 @@ snapshots: dependencies: readdirp: 5.0.0 + chownr@3.0.0: {} + ci-info@3.9.0: {} ci-info@4.3.1: {} @@ -4782,6 +5429,8 @@ snapshots: common-ancestor-path@1.0.1: {} + consola@3.4.2: {} + cookie-es@1.2.2: {} cookie@1.1.1: {} @@ -4894,6 +5543,8 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 + dotenv@16.6.1: {} + dset@3.1.4: {} dunder-proto@1.0.1: @@ -4902,6 +5553,8 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + eastasianwidth@0.2.0: {} + emmet@2.4.11: dependencies: '@emmetio/abbreviation': 2.3.3 @@ -4911,6 +5564,8 @@ snapshots: emoji-regex@8.0.0: {} + emoji-regex@9.2.2: {} + enhanced-resolve@5.18.4: dependencies: graceful-fs: 4.2.11 @@ -4985,6 +5640,35 @@ snapshots: '@esbuild/win32-ia32': 0.25.12 '@esbuild/win32-x64': 0.25.12 + esbuild@0.27.4: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.4 + '@esbuild/android-arm': 0.27.4 + '@esbuild/android-arm64': 0.27.4 + '@esbuild/android-x64': 0.27.4 + '@esbuild/darwin-arm64': 0.27.4 + '@esbuild/darwin-x64': 0.27.4 + '@esbuild/freebsd-arm64': 0.27.4 + '@esbuild/freebsd-x64': 0.27.4 + '@esbuild/linux-arm': 0.27.4 + '@esbuild/linux-arm64': 0.27.4 + '@esbuild/linux-ia32': 0.27.4 + '@esbuild/linux-loong64': 0.27.4 + '@esbuild/linux-mips64el': 0.27.4 + '@esbuild/linux-ppc64': 0.27.4 + '@esbuild/linux-riscv64': 0.27.4 + '@esbuild/linux-s390x': 0.27.4 + '@esbuild/linux-x64': 0.27.4 + '@esbuild/netbsd-arm64': 0.27.4 + '@esbuild/netbsd-x64': 0.27.4 + '@esbuild/openbsd-arm64': 0.27.4 + '@esbuild/openbsd-x64': 0.27.4 + '@esbuild/openharmony-arm64': 0.27.4 + '@esbuild/sunos-x64': 0.27.4 + '@esbuild/win32-arm64': 0.27.4 + '@esbuild/win32-ia32': 0.27.4 + '@esbuild/win32-x64': 0.27.4 + escalade@3.2.0: {} escape-string-regexp@5.0.0: {} @@ -5030,6 +5714,10 @@ snapshots: expect-type@1.3.0: {} + extend-shallow@2.0.1: + dependencies: + is-extendable: 0.1.1 + extend@3.0.2: {} extendable-error@0.1.7: {} @@ -5044,6 +5732,9 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 + fast-json-stable-stringify@2.1.0: + optional: true + fast-uri@3.1.0: {} fastq@1.20.1: @@ -5054,6 +5745,8 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + file-uri-to-path@1.0.0: {} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -5077,6 +5770,11 @@ snapshots: dependencies: is-callable: 1.2.7 + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + fs-extra@11.3.3: dependencies: graceful-fs: 4.2.11 @@ -5124,12 +5822,25 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 + get-tsconfig@4.13.7: + dependencies: + resolve-pkg-maps: 1.0.0 + github-slugger@2.0.0: {} glob-parent@5.1.2: dependencies: is-glob: 4.0.3 + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.9 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + globby@11.1.0: dependencies: array-union: 2.1.0 @@ -5143,6 +5854,13 @@ snapshots: graceful-fs@4.2.11: {} + gray-matter@4.0.3: + dependencies: + js-yaml: 3.14.2 + kind-of: 6.0.3 + section-matter: 1.0.0 + strip-bom-string: 1.0.0 + h3@1.15.5: dependencies: cookie-es: 1.2.2 @@ -5313,6 +6031,13 @@ snapshots: http-cache-semantics@4.2.0: {} + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + human-id@4.1.3: {} iconify-icon@3.0.2: @@ -5355,6 +6080,8 @@ snapshots: is-docker@3.0.0: {} + is-extendable@0.1.1: {} + is-extglob@2.1.1: {} is-fullwidth-code-point@3.0.0: {} @@ -5408,6 +6135,12 @@ snapshots: isexe@2.0.0: {} + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + jiti@2.6.1: {} js-tokens@4.0.0: {} @@ -5425,6 +6158,9 @@ snapshots: jsdoc-type-pratt-parser@4.8.0: {} + json-schema-traverse@0.4.1: + optional: true + json-schema-traverse@1.0.0: {} jsonc-parser@2.3.1: {} @@ -5441,6 +6177,8 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + kind-of@6.0.3: {} + kleur@3.0.3: {} kleur@4.1.5: {} @@ -5508,6 +6246,8 @@ snapshots: loupe@3.2.1: {} + lru-cache@10.4.3: {} + lru-cache@11.2.4: {} lz-string@1.5.0: {} @@ -5980,6 +6720,16 @@ snapshots: min-indent@1.0.1: {} + minimatch@9.0.9: + dependencies: + brace-expansion: 2.0.2 + + minipass@7.1.3: {} + + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 + mri@1.2.0: {} mrmime@2.0.1: {} @@ -5998,8 +6748,18 @@ snapshots: node-fetch-native@1.6.7: {} + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-gyp-build@4.8.4: {} + node-mock-http@1.0.4: {} + nopt@8.1.0: + dependencies: + abbrev: 3.0.1 + normalize-path@3.0.0: {} nth-check@2.1.1: @@ -6057,6 +6817,8 @@ snapshots: p-try@2.2.0: {} + package-json-from-dist@1.0.1: {} + package-manager-detector@0.2.11: dependencies: quansync: 0.2.11 @@ -6101,6 +6863,15 @@ snapshots: path-key@3.1.1: {} + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + + path-to-regexp@6.1.0: {} + + path-to-regexp@6.3.0: {} + path-type@4.0.0: {} pathe@2.0.3: {} @@ -6150,6 +6921,9 @@ snapshots: property-information@7.1.0: {} + punycode@2.3.1: + optional: true + quansync@0.2.11: {} queue-microtask@1.2.3: {} @@ -6327,6 +7101,8 @@ snapshots: resolve-from@5.0.0: {} + resolve-pkg-maps@1.0.0: {} + retext-latin@4.0.0: dependencies: '@types/nlcst': 2.0.3 @@ -6401,6 +7177,11 @@ snapshots: scheduler@0.27.0: {} + section-matter@1.0.0: + dependencies: + extend-shallow: 2.0.1 + kind-of: 6.0.3 + semver@7.7.3: {} set-function-length@1.2.2: @@ -6506,6 +7287,12 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.2 + string-width@7.2.0: dependencies: emoji-regex: 10.6.0 @@ -6525,6 +7312,8 @@ snapshots: dependencies: ansi-regex: 6.2.2 + strip-bom-string@1.0.0: {} + strip-bom@3.0.0: {} strip-indent@3.0.0: @@ -6561,6 +7350,14 @@ snapshots: tapable@2.3.0: {} + tar@7.5.13: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 + term-size@2.2.1: {} tiny-inflate@1.0.3: {} @@ -6592,6 +7389,8 @@ snapshots: dependencies: is-number: 7.0.0 + tr46@0.0.3: {} + trim-lines@3.0.1: {} trough@2.2.0: {} @@ -6604,6 +7403,13 @@ snapshots: tslib@2.8.1: {} + tsx@4.21.0: + dependencies: + esbuild: 0.27.4 + get-tsconfig: 4.13.7 + optionalDependencies: + fsevents: 2.3.3 + type-fest@4.41.0: {} typesafe-path@0.2.2: {} @@ -6693,7 +7499,7 @@ snapshots: acorn: 8.15.0 webpack-virtual-modules: 0.6.2 - unstorage@1.17.4: + unstorage@1.17.4(@vercel/functions@2.2.13): dependencies: anymatch: 3.1.3 chokidar: 5.0.0 @@ -6703,6 +7509,13 @@ snapshots: node-fetch-native: 1.6.7 ofetch: 1.5.1 ufo: 1.6.3 + optionalDependencies: + '@vercel/functions': 2.2.13 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + optional: true util@0.12.5: dependencies: @@ -6729,13 +7542,13 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-node@3.2.4(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2): + vite-node@3.2.4(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -6750,7 +7563,7 @@ snapshots: - tsx - yaml - vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2): + vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) @@ -6763,17 +7576,18 @@ snapshots: fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.30.2 + tsx: 4.21.0 yaml: 2.8.2 - vitefu@1.1.1(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)): + vitefu@1.1.1(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)): optionalDependencies: - vite: 6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) - vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) + '@vitest/mocker': 3.2.4(vite@6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -6791,8 +7605,8 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 6.4.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 @@ -6910,8 +7724,15 @@ snapshots: web-namespaces@2.0.1: {} + webidl-conversions@3.0.1: {} + webpack-virtual-modules@0.6.2: {} + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + which-pm-runs@1.1.0: {} which-typed-array@1.1.20: @@ -6943,6 +7764,12 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.1.2 + wrap-ansi@9.0.2: dependencies: ansi-styles: 6.2.3 @@ -6955,6 +7782,8 @@ snapshots: y18n@5.0.8: {} + yallist@5.0.0: {} + yaml-language-server@1.19.2: dependencies: '@vscode/l10n': 0.0.18 diff --git a/scripts/index-content.ts b/scripts/index-content.ts new file mode 100644 index 0000000..aadc078 --- /dev/null +++ b/scripts/index-content.ts @@ -0,0 +1,254 @@ +import dotenv from 'dotenv'; + +dotenv.config({ path: '.env.local' }); +dotenv.config({ path: '.env' }); + +import fs from 'node:fs'; +import path from 'node:path'; +import crypto from 'node:crypto'; +import { fileURLToPath } from 'node:url'; +import matter from 'gray-matter'; +import { Index } from '@upstash/vector'; +import type { ContentChunk, Frontmatter, ChunkSection, IndexManifest } from '../template/src/types/chat'; +import { INDEXING } from '../template/src/types/constants'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const rootDir = path.resolve(__dirname, '..'); +const contentDir = path.join(rootDir, 'template', 'src', 'content'); +const manifestPath = path.join(rootDir, '.index-manifest.json'); + +let vectorIndex: Index; + +function loadManifest(): IndexManifest { + if (fs.existsSync(manifestPath)) { + return JSON.parse(fs.readFileSync(manifestPath, 'utf-8')); + } + return {}; +} + +function saveManifest(manifest: IndexManifest): void { + fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)); +} + +function hashContent(content: string): string { + return crypto.createHash('md5').update(content).digest('hex'); +} + +function getAllMdxFiles(dir: string): string[] { + const files: string[] = []; + + function walkDir(currentPath: string) { + const entries = fs.readdirSync(currentPath, { withFileTypes: true }); + for (const entry of entries) { + const fullPath = path.join(currentPath, entry.name); + if (entry.isDirectory()) { + walkDir(fullPath); + } else if (entry.name.endsWith('.mdx') || entry.name.endsWith('.md')) { + files.push(fullPath); + } + } + } + + if (fs.existsSync(dir)) { + walkDir(dir); + } + return files; +} + +function getUrlFromPath(filePath: string): string { + const relativePath = path.relative(contentDir, filePath); + const parts = relativePath.split(path.sep); + const collection = parts[0]; + + let urlPath = parts.slice(1).join('/'); + urlPath = urlPath.replace(/\/index\.(mdx?|md)$/, '').replace(/\.(mdx?|md)$/, ''); + + if (!urlPath) { + return `/${collection}`; + } + + return `/${collection}/${urlPath}`; +} + +function getCollection(filePath: string): string { + const relativePath = path.relative(contentDir, filePath); + return relativePath.split(path.sep)[0]; +} + +function chunkByHeadings(content: string, frontmatter: Frontmatter): ChunkSection[] { + const chunks: ChunkSection[] = []; + const lines = content.split('\n'); + + let currentSection = frontmatter.title || 'Introduction'; + let currentContent: string[] = []; + + const contextPrefix = frontmatter.description + ? `Document: ${frontmatter.title}\nDescription: ${frontmatter.description}\n\n` + : `Document: ${frontmatter.title}\n\n`; + + for (const line of lines) { + const h2Match = line.match(/^## (.+)$/); + if (h2Match) { + if (currentContent.length > 0) { + const text = currentContent.join('\n').trim(); + if (text.length > INDEXING.MIN_CHUNK_LENGTH) { + chunks.push({ + section: currentSection, + content: contextPrefix + `Section: ${currentSection}\n\n` + text, + }); + } + } + currentSection = h2Match[1]; + currentContent = []; + } else { + currentContent.push(line); + } + } + + if (currentContent.length > 0) { + const text = currentContent.join('\n').trim(); + if (text.length > INDEXING.MIN_CHUNK_LENGTH) { + chunks.push({ + section: currentSection, + content: contextPrefix + `Section: ${currentSection}\n\n` + text, + }); + } + } + + if (chunks.length === 0 && content.trim().length > INDEXING.MIN_CHUNK_LENGTH) { + chunks.push({ + section: currentSection, + content: contextPrefix + content.trim(), + }); + } + + return chunks; +} + +function stripMdxComponents(content: string): string { + content = content.replace(/^import\s+.+from\s+['"].+['"];?\s*$/gm, ''); + content = content.replace(/<[A-Z][a-zA-Z]*[^>]*>/g, ''); + content = content.replace(/<\/[A-Z][a-zA-Z]*>/g, ''); + content = content.replace(/```[\s\S]*?```/g, '[code example]'); + content = content.replace(/\n{3,}/g, '\n\n'); + + return content.trim(); +} + +async function processFile(filePath: string): Promise { + const fileContent = fs.readFileSync(filePath, 'utf-8'); + const { data: frontmatter, content } = matter(fileContent); + + const cleanContent = stripMdxComponents(content); + const url = getUrlFromPath(filePath); + const collection = getCollection(filePath); + const title = (frontmatter.title as string) || path.basename(filePath, path.extname(filePath)); + + const chunks = chunkByHeadings(cleanContent, frontmatter as Frontmatter); + + return chunks.map((chunk, index) => ({ + id: `${url}-${index}`, + content: chunk.content, + metadata: { + title, + section: chunk.section, + url, + collection, + description: frontmatter.description as string | undefined, + }, + })); +} + +async function main() { + console.log('Starting content indexing...\n'); + + if (!process.env.UPSTASH_VECTOR_REST_URL || !process.env.UPSTASH_VECTOR_REST_TOKEN) { + console.error('Error: Upstash Vector credentials are not set'); + console.error('Make sure you have a .env file with UPSTASH_VECTOR_REST_URL and UPSTASH_VECTOR_REST_TOKEN'); + process.exit(1); + } + + vectorIndex = new Index({ + url: process.env.UPSTASH_VECTOR_REST_URL, + token: process.env.UPSTASH_VECTOR_REST_TOKEN, + }); + + const manifest = loadManifest(); + const newManifest: IndexManifest = {}; + + const collections = INDEXING.COLLECTIONS; + const allFiles: string[] = []; + + for (const collection of collections) { + const collectionDir = path.join(contentDir, collection); + const files = getAllMdxFiles(collectionDir); + allFiles.push(...files); + console.log(`Found ${files.length} files in ${collection}/`); + } + + console.log(`\nTotal files to process: ${allFiles.length}\n`); + + const allChunks: ContentChunk[] = []; + const changedChunks: ContentChunk[] = []; + + for (const file of allFiles) { + const chunks = await processFile(file); + allChunks.push(...chunks); + + for (const chunk of chunks) { + const hash = hashContent(chunk.content); + newManifest[chunk.id] = hash; + + if (manifest[chunk.id] !== hash) { + changedChunks.push(chunk); + } + } + + console.log(`Processed: ${path.relative(contentDir, file)} (${chunks.length} chunks)`); + } + + const deletedIds = Object.keys(manifest).filter((id) => !newManifest[id]); + + console.log(`\nTotal chunks: ${allChunks.length}`); + console.log(`Changed/New: ${changedChunks.length}`); + console.log(`Deleted: ${deletedIds.length}\n`); + + if (changedChunks.length === 0 && deletedIds.length === 0) { + console.log('No changes detected. Skipping upload.\n'); + return; + } + + if (deletedIds.length > 0) { + console.log('Removing deleted content...'); + for (const id of deletedIds) { + await vectorIndex.delete(id); + } + console.log(`Removed ${deletedIds.length} vectors\n`); + } + + if (changedChunks.length > 0) { + console.log('Uploading changed content...\n'); + + for (let i = 0; i < changedChunks.length; i += INDEXING.BATCH_SIZE) { + const batch = changedChunks.slice(i, i + INDEXING.BATCH_SIZE); + + const vectors = batch.map((chunk) => ({ + id: chunk.id, + data: chunk.content, + metadata: chunk.metadata as unknown as Record, + })); + + await vectorIndex.upsert(vectors); + console.log(`Uploaded batch ${Math.floor(i / INDEXING.BATCH_SIZE) + 1}/${Math.ceil(changedChunks.length / INDEXING.BATCH_SIZE)}`); + + if (i + INDEXING.BATCH_SIZE < changedChunks.length) { + await new Promise((resolve) => setTimeout(resolve, INDEXING.BATCH_DELAY_MS)); + } + } + } + + saveManifest(newManifest); + console.log('\nContent indexing complete!'); +} + +main().catch(console.error); diff --git a/scripts/index-watch.ts b/scripts/index-watch.ts new file mode 100644 index 0000000..862abfa --- /dev/null +++ b/scripts/index-watch.ts @@ -0,0 +1,214 @@ +import dotenv from 'dotenv'; + +dotenv.config({ path: '.env.local' }); +dotenv.config({ path: '.env' }); + +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import matter from 'gray-matter'; +import { Index } from '@upstash/vector'; +import type { Frontmatter, ChunkSection } from '../template/src/types/chat'; +import { INDEXING } from '../template/src/types/constants'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const rootDir = path.resolve(__dirname, '..'); +const contentDir = path.join(rootDir, 'template', 'src', 'content'); + +let vectorIndex: Index; +const debounceTimers = new Map(); + +function getUrlFromPath(filePath: string): string { + const relativePath = path.relative(contentDir, filePath); + const parts = relativePath.split(path.sep); + const collection = parts[0]; + + let urlPath = parts.slice(1).join('/'); + urlPath = urlPath.replace(/\/index\.(mdx?|md)$/, '').replace(/\.(mdx?|md)$/, ''); + + if (!urlPath) { + return `/${collection}`; + } + + return `/${collection}/${urlPath}`; +} + +function getCollection(filePath: string): string { + const relativePath = path.relative(contentDir, filePath); + return relativePath.split(path.sep)[0]; +} + +function chunkByHeadings(content: string, frontmatter: Frontmatter): ChunkSection[] { + const chunks: ChunkSection[] = []; + const lines = content.split('\n'); + + let currentSection = frontmatter.title || 'Introduction'; + let currentContent: string[] = []; + + const contextPrefix = frontmatter.description + ? `Document: ${frontmatter.title}\nDescription: ${frontmatter.description}\n\n` + : `Document: ${frontmatter.title}\n\n`; + + for (const line of lines) { + const h2Match = line.match(/^## (.+)$/); + if (h2Match) { + if (currentContent.length > 0) { + const text = currentContent.join('\n').trim(); + if (text.length > INDEXING.MIN_CHUNK_LENGTH) { + chunks.push({ + section: currentSection, + content: contextPrefix + `Section: ${currentSection}\n\n` + text, + }); + } + } + currentSection = h2Match[1]; + currentContent = []; + } else { + currentContent.push(line); + } + } + + if (currentContent.length > 0) { + const text = currentContent.join('\n').trim(); + if (text.length > INDEXING.MIN_CHUNK_LENGTH) { + chunks.push({ + section: currentSection, + content: contextPrefix + `Section: ${currentSection}\n\n` + text, + }); + } + } + + if (chunks.length === 0 && content.trim().length > INDEXING.MIN_CHUNK_LENGTH) { + chunks.push({ + section: currentSection, + content: contextPrefix + content.trim(), + }); + } + + return chunks; +} + +function stripMdxComponents(content: string): string { + content = content.replace(/^import\s+.+from\s+['"].+['"];?\s*$/gm, ''); + content = content.replace(/<[A-Z][a-zA-Z]*[^>]*>/g, ''); + content = content.replace(/<\/[A-Z][a-zA-Z]*>/g, ''); + content = content.replace(/```[\s\S]*?```/g, '[code example]'); + content = content.replace(/\n{3,}/g, '\n\n'); + + return content.trim(); +} + +async function indexFile(filePath: string): Promise { + const fileContent = fs.readFileSync(filePath, 'utf-8'); + const { data: frontmatter, content } = matter(fileContent); + + const cleanContent = stripMdxComponents(content); + const url = getUrlFromPath(filePath); + const collection = getCollection(filePath); + const title = (frontmatter.title as string) || path.basename(filePath, path.extname(filePath)); + + const chunks = chunkByHeadings(cleanContent, frontmatter as Frontmatter); + + const vectors = chunks.map((chunk, index) => ({ + id: `${url}-${index}`, + data: chunk.content, + metadata: { + title, + section: chunk.section, + url, + collection, + description: frontmatter.description as string | undefined, + } as Record, + })); + + if (vectors.length > 0) { + await vectorIndex.upsert(vectors); + } + + console.log(`[${new Date().toLocaleTimeString()}] Indexed: ${path.relative(contentDir, filePath)} (${vectors.length} chunks)`); +} + +async function deleteFileFromIndex(filePath: string): Promise { + const url = getUrlFromPath(filePath); + const maxChunks = 100; // Support up to 100 chunks per document + const idsToDelete: string[] = []; + + for (let i = 0; i < maxChunks; i++) { + idsToDelete.push(`${url}-${i}`); + } + + // Delete in batches, ignoring errors for non-existent IDs + try { + await vectorIndex.delete(idsToDelete); + } catch { + // Some IDs may not exist, which is expected + } + + console.log(`[${new Date().toLocaleTimeString()}] Removed from index: ${path.relative(contentDir, filePath)}`); +} + +function handleFileChange(filePath: string, eventType: string): void { + if (!filePath.endsWith('.mdx') && !filePath.endsWith('.md')) return; + + const existingTimer = debounceTimers.get(filePath); + if (existingTimer) { + clearTimeout(existingTimer); + } + + const timer = setTimeout(async () => { + debounceTimers.delete(filePath); + + try { + if (eventType === 'rename' && !fs.existsSync(filePath)) { + await deleteFileFromIndex(filePath); + } else { + await indexFile(filePath); + } + } catch (error) { + console.error(`Error processing ${filePath}:`, error); + } + }, INDEXING.DEBOUNCE_MS); + + debounceTimers.set(filePath, timer); +} + +function watchDirectory(dir: string): void { + if (!fs.existsSync(dir)) return; + + fs.watch(dir, { recursive: true }, (eventType, filename) => { + if (!filename) return; + const filePath = path.join(dir, filename); + handleFileChange(filePath, eventType); + }); +} + +async function main(): Promise { + console.log('Starting content index watcher...\n'); + + if (!process.env.UPSTASH_VECTOR_REST_URL || !process.env.UPSTASH_VECTOR_REST_TOKEN) { + console.error('Error: Upstash Vector credentials are not set'); + process.exit(1); + } + + vectorIndex = new Index({ + url: process.env.UPSTASH_VECTOR_REST_URL, + token: process.env.UPSTASH_VECTOR_REST_TOKEN, + }); + + const collections = INDEXING.COLLECTIONS; + + for (const collection of collections) { + const collectionDir = path.join(contentDir, collection); + watchDirectory(collectionDir); + console.log(`Watching: ${collection}/`); + } + + console.log('\nWaiting for file changes... (Ctrl+C to stop)\n'); + + process.on('SIGINT', () => { + console.log('\nStopping watcher...'); + process.exit(0); + }); +} + +main().catch(console.error); diff --git a/template/src/components/AskAnything.astro b/template/src/components/AskAnything.astro new file mode 100644 index 0000000..45b06e0 --- /dev/null +++ b/template/src/components/AskAnything.astro @@ -0,0 +1,115 @@ +--- +import { CHAT_DEFAULTS, CHAT } from '@/types/constants'; + +interface Props { + subtitle?: string; + suggestions?: string[]; +} + +const { + subtitle = CHAT_DEFAULTS.ASK_ANYTHING.subtitle, + suggestions = CHAT_DEFAULTS.ASK_ANYTHING.suggestions +} = Astro.props; +--- + +
+

{subtitle}

+ +
+
+ + +
+
+ +
+
+
+
+ +
+ {suggestions.map((suggestion) => ( + + ))} +
+
+ + diff --git a/template/src/components/ChatWidget.astro b/template/src/components/ChatWidget.astro new file mode 100644 index 0000000..49433c2 --- /dev/null +++ b/template/src/components/ChatWidget.astro @@ -0,0 +1,320 @@ +--- +import { CHAT } from '@/types/constants'; +--- + + + + + + diff --git a/template/src/components/Hero.astro b/template/src/components/Hero.astro index 961a854..c57bf61 100644 --- a/template/src/components/Hero.astro +++ b/template/src/components/Hero.astro @@ -1,6 +1,5 @@ --- import Cloud from './Cloud.astro'; -import Button from './Button.astro'; import type { HeroProps } from '@/types'; import { DEFAULTS } from '@/types/constants'; @@ -9,8 +8,6 @@ const { title, highlightedText, description, - buttonText = DEFAULTS.HERO.buttonText, - buttonHref = DEFAULTS.HERO.buttonHref, } = Astro.props as HeroProps; --- @@ -33,10 +30,6 @@ const { {description}

- - diff --git a/template/src/pages/api/chat.ts b/template/src/pages/api/chat.ts new file mode 100644 index 0000000..59076fe --- /dev/null +++ b/template/src/pages/api/chat.ts @@ -0,0 +1,177 @@ +import type { APIRoute } from 'astro'; +import { Index } from '@upstash/vector'; +import { GoogleGenerativeAI } from '@google/generative-ai'; +import type { ChatContext, VectorMetadata, GeminiMessage } from '@/types/chat'; +import { CHAT, SITE_TITLE } from '@/types/constants'; + +export const prerender = false; + +function getVectorIndex(): Index | null { + const url = import.meta.env.UPSTASH_VECTOR_REST_URL; + const token = import.meta.env.UPSTASH_VECTOR_REST_TOKEN; + + if (!url || !token) { + return null; + } + + return new Index({ url, token }); +} + +async function searchSimilarContent(vectorIndex: Index, query: string, topK: number = CHAT.VECTOR_TOP_K): Promise { + const results = await vectorIndex.query({ + data: query, + topK, + includeMetadata: true, + includeData: true, + }); + + return results.map((result) => { + const metadata = result.metadata as unknown as VectorMetadata; + const chunkContent = typeof result.data === 'string' ? result.data : ''; + return { + content: chunkContent || `Title: ${metadata.title}\nSection: ${metadata.section}\nURL: ${metadata.url}`, + title: metadata.title, + section: metadata.section, + url: metadata.url, + }; + }); +} + +function buildSystemPrompt(contexts: ChatContext[]): string { + const contextText = contexts + .map((ctx, i) => `[${i + 1}] ${ctx.title} - ${ctx.section}\nURL: ${ctx.url}`) + .join('\n\n'); + + return `You are a helpful documentation assistant for ${SITE_TITLE}, a documentation template built with Astro. +Your role is to answer questions based on the documentation content provided. + +Guidelines: +- Be concise and helpful +- Reference specific documentation sections when relevant +- If the answer isn't in the provided context, say so honestly +- Include relevant URLs when they would be helpful +- Format responses using markdown for readability +- For code examples, use proper code blocks with language tags + +Relevant documentation sections: +${contextText} + +Answer the user's question based on the above context. If the question cannot be answered from the provided context, let them know and suggest they check the documentation directly.`; +} + +export const POST: APIRoute = async ({ request }) => { + try { + const apiKey = import.meta.env.GOOGLE_GENERATIVE_AI_API_KEY; + + if (!apiKey) { + return new Response(JSON.stringify({ + error: 'GOOGLE_GENERATIVE_AI_API_KEY is not configured' + }), { + status: 500, + headers: { 'Content-Type': 'application/json' }, + }); + } + + const vectorIndex = getVectorIndex(); + if (!vectorIndex) { + return new Response(JSON.stringify({ + error: 'Upstash Vector credentials are not configured' + }), { + status: 500, + headers: { 'Content-Type': 'application/json' }, + }); + } + + const { message, history = [] } = await request.json(); + + if (!message || typeof message !== 'string') { + return new Response(JSON.stringify({ error: 'Message is required' }), { + status: 400, + headers: { 'Content-Type': 'application/json' }, + }); + } + + const contexts = await searchSimilarContent(vectorIndex, message); + const systemPrompt = buildSystemPrompt(contexts); + + const genAI = new GoogleGenerativeAI(apiKey); + const chatHistory: GeminiMessage[] = history.map((msg: { role: string; content: string }) => ({ + role: msg.role === 'assistant' ? 'model' : 'user', + parts: [{ text: msg.content }], + })); + + let result; + let lastError; + + for (const modelName of CHAT.MODELS) { + try { + const model = genAI.getGenerativeModel({ + model: modelName, + systemInstruction: systemPrompt, + }); + const chat = model.startChat({ history: chatHistory }); + result = await chat.sendMessageStream(message); + console.log(`Using model: ${modelName}`); + break; + } catch (error) { + lastError = error; + const errorMessage = error instanceof Error ? error.message : ''; + // Check for rate limit errors (429 or quota exceeded) + if (errorMessage.includes('429') || errorMessage.includes('quota') || errorMessage.includes('rate') || errorMessage.includes('RESOURCE_EXHAUSTED')) { + console.log(`Rate limited on ${modelName}, trying next...`); + continue; + } + throw error; + } + } + + if (!result) { + return new Response(JSON.stringify({ + error: 'rate_limit', + message: CHAT.RATE_LIMIT_MESSAGE, + }), { + status: 429, + headers: { 'Content-Type': 'application/json' }, + }); + } + + const encoder = new TextEncoder(); + const stream = new ReadableStream({ + async start(controller) { + try { + for await (const chunk of result.stream) { + const text = chunk.text(); + if (text) { + controller.enqueue(encoder.encode(`data: ${JSON.stringify({ type: 'text-delta', textDelta: text })}\n\n`)); + } + } + controller.enqueue(encoder.encode(`data: [DONE]\n\n`)); + controller.close(); + } catch (streamError) { + console.error('Stream error:', streamError); + controller.error(streamError); + } + }, + }); + + return new Response(stream, { + headers: { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + 'Connection': 'keep-alive', + }, + }); + } catch (error) { + console.error('Chat API error:', error); + return new Response( + JSON.stringify({ + error: 'An error occurred processing your request', + details: error instanceof Error ? error.message : 'Unknown error', + }), + { + status: 500, + headers: { 'Content-Type': 'application/json' }, + } + ); + } +}; diff --git a/template/src/pages/chat/[uid].astro b/template/src/pages/chat/[uid].astro new file mode 100644 index 0000000..e6949fd --- /dev/null +++ b/template/src/pages/chat/[uid].astro @@ -0,0 +1,347 @@ +--- +import BaseLayout from '@/layouts/BaseLayout.astro'; +import Header from '@/components/Header.astro'; + +export const prerender = false; + +const { uid } = Astro.params; +--- + + +
+ +
+
+ +
+ +
+ + +
+
+ + +
+
+
+
+ + + + + diff --git a/template/src/pages/index.astro b/template/src/pages/index.astro index 0c39f47..a127a15 100644 --- a/template/src/pages/index.astro +++ b/template/src/pages/index.astro @@ -5,6 +5,7 @@ import Footer from '@/components/Footer.astro'; import Hero from '@/components/Hero.astro'; import FeatureCard from '@/components/FeatureCard.astro'; import SectionHeader from '@/components/SectionHeader.astro'; +import AskAnything from '@/components/AskAnything.astro'; --- @@ -17,6 +18,13 @@ import SectionHeader from '@/components/SectionHeader.astro'; description="Build documentation that scales with your product. A modern, structured documentation template designed for clarity, consistency, and long-term growth." /> + +
+
+ +
+
+
diff --git a/template/src/types/chat.ts b/template/src/types/chat.ts new file mode 100644 index 0000000..8e70ba2 --- /dev/null +++ b/template/src/types/chat.ts @@ -0,0 +1,65 @@ +import type { ChatRole } from './constants'; + +export interface ChatMessage { + id: string; + role: ChatRole; + content: string; + timestamp: number; +} + +export interface ChatRequest { + message: string; + history?: ChatMessage[]; +} + +export interface ChatResponse { + error?: string; + details?: string; +} + +export interface VectorMetadata { + title: string; + section: string; + url: string; + collection: string; + description?: string; +} + +export interface VectorSearchResult { + id: string; + score: number; + metadata: VectorMetadata; +} + +export interface ChatContext { + content: string; + title: string; + section: string; + url: string; +} + +export interface GeminiMessage { + role: 'user' | 'model'; + parts: { text: string }[]; +} + +export interface ContentChunk { + id: string; + content: string; + metadata: VectorMetadata; +} + +export interface Frontmatter { + title?: string; + description?: string; + [key: string]: unknown; +} + +export interface ChunkSection { + section: string; + content: string; +} + +export interface IndexManifest { + [id: string]: string; +} diff --git a/template/src/types/components.ts b/template/src/types/components.ts index f227c3f..878edb5 100644 --- a/template/src/types/components.ts +++ b/template/src/types/components.ts @@ -3,8 +3,6 @@ export interface HeroProps { title: string; highlightedText?: string; description: string; - buttonText?: string; - buttonHref?: string; } export interface CalloutProps { diff --git a/template/src/types/constants.ts b/template/src/types/constants.ts index 0dca849..47fc957 100644 --- a/template/src/types/constants.ts +++ b/template/src/types/constants.ts @@ -113,3 +113,58 @@ export const LAYOUT = { stickyTop: 'sticky top-28', collapsedMargin: '-12rem', } as const; + +export const CHAT = { + API_ENDPOINT: '/api/chat', + MODELS: ['gemini-2.5-flash', 'gemini-2.5-flash-lite', 'gemini-3.0-flash'], + VECTOR_TOP_K: 5, + TEXTAREA_MAX_HEIGHT: 150, + WELCOME_MESSAGE: "Hi! I'm your DocuBase assistant. Ask me anything about the documentation, components, or how to get started.", + ASSISTANT_NAME: 'DocuBase Assistant', + RATE_LIMIT_MESSAGE: "I've reached my daily limit. Please try again tomorrow!", +} as const; + +export const INDEXING = { + BATCH_SIZE: 10, + BATCH_DELAY_MS: 500, + DEBOUNCE_MS: 1000, + MIN_CHUNK_LENGTH: 50, + COLLECTIONS: ['docs', 'blog', 'tutorials'], +} as const; + +export const CHAT_DEFAULTS = { + ASK_ANYTHING: { + subtitle: 'Meet AI Chat Mode', + placeholder: 'Ask anything', + suggestions: [ + 'How do I get started with DocuBase?', + 'What components are available?', + 'How to deploy my docs?', + ], + }, +} as const; + +export const CHAT_SELECTORS = { + askForm: 'ask-form', + askInput: 'ask-input', + askSubmit: 'ask-submit', + suggestionChip: '.suggestion-chip', + chatTrigger: 'chat-trigger', + chatModal: 'chat-modal', + chatBackdrop: 'chat-backdrop', + chatClose: 'chat-close', + chatForm: 'chat-form', + chatInput: 'chat-input', + chatSubmit: 'chat-submit', + chatMessages: 'chat-messages', + sendIcon: 'send-icon', + loadingIcon: 'loading-icon', +} as const; + +export const CHAT_ROLES = { + user: 'user', + assistant: 'assistant', + model: 'model', +} as const; + +export type ChatRole = (typeof CHAT_ROLES)[keyof typeof CHAT_ROLES]; diff --git a/template/src/types/index.ts b/template/src/types/index.ts index fcbeac9..d1e2101 100644 --- a/template/src/types/index.ts +++ b/template/src/types/index.ts @@ -3,3 +3,4 @@ export * from './layout'; export * from './constants'; export * from './styles'; export * from './icons'; +export * from './chat';