From 8cceec4cddf18d68ad67bb71b171da142b03e018 Mon Sep 17 00:00:00 2001
From: Sithumli Nanayakkara
Date: Wed, 25 Mar 2026 21:44:27 +0530
Subject: [PATCH 1/4] feat: add chat functionality with AI assistant and
content indexing
---
.changeset/ai-chat-feature.md | 12 +
.changeset/plain-grapes-notice.md | 5 +
.env.example | 8 +
.gitignore | 1 +
README.md | 40 +
astro.config.mjs | 2 +
package.json | 17 +-
pnpm-lock.yaml | 1044 ++++++++++++++++++++-
scripts/index-content.ts | 254 +++++
scripts/index-watch.ts | 209 +++++
template/src/components/AskAnything.astro | 105 +++
template/src/components/ChatWidget.astro | 297 ++++++
template/src/components/Hero.astro | 4 -
template/src/pages/api/chat.ts | 132 +++
template/src/pages/chat/[uid].astro | 325 +++++++
template/src/pages/index.astro | 8 +
template/src/types/chat.ts | 65 ++
template/src/types/constants.ts | 54 ++
template/src/types/index.ts | 1 +
19 files changed, 2547 insertions(+), 36 deletions(-)
create mode 100644 .changeset/ai-chat-feature.md
create mode 100644 .changeset/plain-grapes-notice.md
create mode 100644 .env.example
create mode 100644 scripts/index-content.ts
create mode 100644 scripts/index-watch.ts
create mode 100644 template/src/components/AskAnything.astro
create mode 100644 template/src/components/ChatWidget.astro
create mode 100644 template/src/pages/api/chat.ts
create mode 100644 template/src/pages/chat/[uid].astro
create mode 100644 template/src/types/chat.ts
diff --git a/.changeset/ai-chat-feature.md b/.changeset/ai-chat-feature.md
new file mode 100644
index 0000000..00c96c5
--- /dev/null
+++ b/.changeset/ai-chat-feature.md
@@ -0,0 +1,12 @@
+---
+"create-docubase": minor
+---
+
+Add AI chat assistant with RAG-powered documentation search
+
+- Add AskAnything component for homepage chat input
+- Add ChatWidget floating chat button for all pages
+- Add chat API endpoint with Google Gemini integration
+- Add Upstash Vector for semantic search
+- Auto-index content on build
+- Add index-content:watch script for development
diff --git a/.changeset/plain-grapes-notice.md b/.changeset/plain-grapes-notice.md
new file mode 100644
index 0000000..5d30e18
--- /dev/null
+++ b/.changeset/plain-grapes-notice.md
@@ -0,0 +1,5 @@
+---
+"create-docubase": minor
+---
+
+AI chat assistant with RAG-powered documentation search
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..a9c2bc3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -54,6 +54,7 @@ coverage/
# Cache
.cache/
*.tsbuildinfo
+.index-manifest.json
# Temporary files
tmp/
diff --git a/README.md b/README.md
index e529726..2b0320e 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 available on all pages
+
## 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..0c41501 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,8 @@
],
"scripts": {
"dev": "astro dev",
- "build": "astro build && pagefind --site dist",
+ "build": "pnpm run index-content && astro build && pagefind --site dist",
+ "build:no-index": "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",
@@ -39,11 +42,18 @@
"author": "",
"license": "MIT",
"dependencies": {
+ "@ai-sdk/google": "^1.2.22",
"@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",
+ "ai": "^4.3.19",
"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 +73,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..d03680e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,21 +8,42 @@ importers:
.:
dependencies:
+ '@ai-sdk/google':
+ specifier: ^1.2.22
+ version: 1.2.22(zod@3.25.76)
'@astrojs/check':
specifier: ^0.9.6
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
+ ai:
+ specifier: ^4.3.19
+ version: 4.3.19(react@19.2.3)(zod@3.25.76)
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 +83,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,15 +102,50 @@ 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:
'@adobe/css-tools@4.4.4':
resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==}
+ '@ai-sdk/google@1.2.22':
+ resolution: {integrity: sha512-Ppxu3DIieF1G9pyQ5O1Z646GYR0gkC57YdBqXJ82qvCdhEhZHu0TWhmnOoeIWe2olSbuDeoOY+MfJrW8dzS3Hw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.0.0
+
+ '@ai-sdk/provider-utils@2.2.8':
+ resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.23.8
+
+ '@ai-sdk/provider@1.1.3':
+ resolution: {integrity: sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==}
+ engines: {node: '>=18'}
+
+ '@ai-sdk/react@1.2.12':
+ resolution: {integrity: sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ react: ^18 || ^19 || ^19.0.0-rc
+ zod: ^3.23.8
+ peerDependenciesMeta:
+ zod:
+ optional: true
+
+ '@ai-sdk/ui-utils@1.2.11':
+ resolution: {integrity: sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.23.8
+
'@astrojs/check@0.9.6':
resolution: {integrity: sha512-jlaEu5SxvSgmfGIFfNgcn5/f+29H61NJzEMfAZ82Xopr4XBchXB1GVlcJsE+elUlsYSbXlptZLX+JMG3b/wZEA==}
hasBin: true
@@ -99,6 +155,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 +190,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 +312,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 +771,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 +801,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==}
@@ -590,6 +827,10 @@ packages:
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
+ '@opentelemetry/api@1.9.0':
+ resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
+ engines: {node: '>=8.0.0'}
+
'@oslojs/encoding@1.1.0':
resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==}
@@ -623,6 +864,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'}
@@ -1063,6 +1308,9 @@ packages:
'@types/deep-eql@4.0.2':
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
+ '@types/diff-match-patch@1.0.36':
+ resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==}
+
'@types/estree-jsx@1.0.5':
resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
@@ -1114,6 +1362,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 +1485,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 +1504,20 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
+ agent-base@7.1.4:
+ resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
+ engines: {node: '>= 14'}
+
+ ai@4.3.19:
+ resolution: {integrity: sha512-dIE2bfNpqHN3r6IINp9znguYdhIOheKW2LDigAMrgt/upT3B8eBGPSCblENvaZGoq+hxaN9fSMzjWpbqloP+7Q==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ react: ^18 || ^19 || ^19.0.0-rc
+ zod: ^3.23.8
+ peerDependenciesMeta:
+ react:
+ optional: true
+
ajv-draft-04@1.0.0:
resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==}
peerDependencies:
@@ -1205,6 +1526,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 +1600,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 +1614,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 +1628,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 +1638,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 +1714,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 +1758,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==}
@@ -1514,6 +1858,9 @@ packages:
devlop@1.1.0:
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+ diff-match-patch@1.0.5:
+ resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==}
+
diff@8.0.3:
resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==}
engines: {node: '>=0.3.1'}
@@ -1544,6 +1891,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 +1903,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 +1915,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 +1965,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 +2014,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 +2031,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 +2049,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 +2075,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 +2119,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 +2129,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 +2145,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 +2222,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 +2284,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 +2347,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,21 +2372,36 @@ 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==}
+ json-schema@0.4.0:
+ resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
+
jsonc-parser@2.3.1:
resolution: {integrity: sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==}
jsonc-parser@3.3.1:
resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==}
+ jsondiffpatch@0.6.0:
+ resolution: {integrity: sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+
jsonfile@4.0.0:
resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
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 +2499,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 +2707,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 +2748,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 +2827,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 +2860,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 +2940,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 +3063,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 +3104,13 @@ 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'}
+
+ secure-json-parse@2.7.0:
+ resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==}
+
semver@7.7.3:
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
engines: {node: '>=10'}
@@ -2719,6 +3193,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 +3212,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'}
@@ -2760,6 +3242,11 @@ packages:
engines: {node: '>=16'}
hasBin: true
+ swr@2.4.1:
+ resolution: {integrity: sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA==}
+ peerDependencies:
+ react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
tailwindcss@4.1.18:
resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==}
@@ -2767,10 +3254,18 @@ 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'}
+ throttleit@2.1.0:
+ resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==}
+ engines: {node: '>=18'}
+
tiny-inflate@1.0.3:
resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==}
@@ -2815,6 +3310,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 +3336,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 +3478,14 @@ packages:
uploadthing:
optional: true
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+ use-sync-external-store@1.6.0:
+ resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
util@0.12.5:
resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==}
@@ -3167,9 +3678,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 +3713,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 +3740,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
@@ -3274,6 +3799,40 @@ snapshots:
'@adobe/css-tools@4.4.4': {}
+ '@ai-sdk/google@1.2.22(zod@3.25.76)':
+ dependencies:
+ '@ai-sdk/provider': 1.1.3
+ '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76)
+ zod: 3.25.76
+
+ '@ai-sdk/provider-utils@2.2.8(zod@3.25.76)':
+ dependencies:
+ '@ai-sdk/provider': 1.1.3
+ nanoid: 3.3.11
+ secure-json-parse: 2.7.0
+ zod: 3.25.76
+
+ '@ai-sdk/provider@1.1.3':
+ dependencies:
+ json-schema: 0.4.0
+
+ '@ai-sdk/react@1.2.12(react@19.2.3)(zod@3.25.76)':
+ dependencies:
+ '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76)
+ '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76)
+ react: 19.2.3
+ swr: 2.4.1(react@19.2.3)
+ throttleit: 2.1.0
+ optionalDependencies:
+ zod: 3.25.76
+
+ '@ai-sdk/ui-utils@1.2.11(zod@3.25.76)':
+ dependencies:
+ '@ai-sdk/provider': 1.1.3
+ '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76)
+ zod: 3.25.76
+ zod-to-json-schema: 3.25.1(zod@3.25.76)
+
'@astrojs/check@0.9.6(prettier@3.8.1)(typescript@5.9.3)':
dependencies:
'@astrojs/language-server': 2.16.3(prettier@3.8.1)(typescript@5.9.3)
@@ -3287,6 +3846,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 +3901,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 +3936,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 +4163,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 +4424,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 +4472,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
@@ -3843,6 +4533,8 @@ snapshots:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.20.1
+ '@opentelemetry/api@1.9.0': {}
+
'@oslojs/encoding@1.1.0': {}
'@pagefind/darwin-arm64@1.4.0':
@@ -3863,6 +4555,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 +4778,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 +4823,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 +4960,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:
@@ -4310,6 +5005,8 @@ snapshots:
'@types/deep-eql@4.0.2': {}
+ '@types/diff-match-patch@1.0.36': {}
+
'@types/estree-jsx@1.0.5':
dependencies:
'@types/estree': 1.0.8
@@ -4364,6 +5061,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 +5117,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 +5226,44 @@ 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: {}
+
+ ai@4.3.19(react@19.2.3)(zod@3.25.76):
+ dependencies:
+ '@ai-sdk/provider': 1.1.3
+ '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76)
+ '@ai-sdk/react': 1.2.12(react@19.2.3)(zod@3.25.76)
+ '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76)
+ '@opentelemetry/api': 1.9.0
+ jsondiffpatch: 0.6.0
+ zod: 3.25.76
+ optionalDependencies:
+ react: 19.2.3
+
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 +5318,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 +5373,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 +5420,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 +5430,8 @@ snapshots:
bail@2.0.2: {}
+ balanced-match@1.0.2: {}
+
base-64@1.0.0: {}
better-opn@3.0.2:
@@ -4672,6 +5442,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 +5459,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 +5532,8 @@ snapshots:
dependencies:
readdirp: 5.0.0
+ chownr@3.0.0: {}
+
ci-info@3.9.0: {}
ci-info@4.3.1: {}
@@ -4782,6 +5562,8 @@ snapshots:
common-ancestor-path@1.0.1: {}
+ consola@3.4.2: {}
+
cookie-es@1.2.2: {}
cookie@1.1.1: {}
@@ -4864,6 +5646,8 @@ snapshots:
dependencies:
dequal: 2.0.3
+ diff-match-patch@1.0.5: {}
+
diff@8.0.3: {}
dir-glob@3.0.1:
@@ -4894,6 +5678,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 +5688,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 +5699,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 +5775,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 +5849,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 +5867,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 +5880,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 +5905,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 +5957,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 +5989,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 +6166,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 +6215,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 +6270,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,12 +6293,23 @@ snapshots:
jsdoc-type-pratt-parser@4.8.0: {}
+ json-schema-traverse@0.4.1:
+ optional: true
+
json-schema-traverse@1.0.0: {}
+ json-schema@0.4.0: {}
+
jsonc-parser@2.3.1: {}
jsonc-parser@3.3.1: {}
+ jsondiffpatch@0.6.0:
+ dependencies:
+ '@types/diff-match-patch': 1.0.36
+ chalk: 5.6.2
+ diff-match-patch: 1.0.5
+
jsonfile@4.0.0:
optionalDependencies:
graceful-fs: 4.2.11
@@ -5441,6 +6320,8 @@ snapshots:
optionalDependencies:
graceful-fs: 4.2.11
+ kind-of@6.0.3: {}
+
kleur@3.0.3: {}
kleur@4.1.5: {}
@@ -5508,6 +6389,8 @@ snapshots:
loupe@3.2.1: {}
+ lru-cache@10.4.3: {}
+
lru-cache@11.2.4: {}
lz-string@1.5.0: {}
@@ -5980,6 +6863,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 +6891,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 +6960,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 +7006,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 +7064,9 @@ snapshots:
property-information@7.1.0: {}
+ punycode@2.3.1:
+ optional: true
+
quansync@0.2.11: {}
queue-microtask@1.2.3: {}
@@ -6327,6 +7244,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 +7320,13 @@ snapshots:
scheduler@0.27.0: {}
+ section-matter@1.0.0:
+ dependencies:
+ extend-shallow: 2.0.1
+ kind-of: 6.0.3
+
+ secure-json-parse@2.7.0: {}
+
semver@7.7.3: {}
set-function-length@1.2.2:
@@ -6506,6 +7432,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 +7457,8 @@ snapshots:
dependencies:
ansi-regex: 6.2.2
+ strip-bom-string@1.0.0: {}
+
strip-bom@3.0.0: {}
strip-indent@3.0.0:
@@ -6557,12 +7491,28 @@ snapshots:
picocolors: 1.1.1
sax: 1.4.4
+ swr@2.4.1(react@19.2.3):
+ dependencies:
+ dequal: 2.0.3
+ react: 19.2.3
+ use-sync-external-store: 1.6.0(react@19.2.3)
+
tailwindcss@4.1.18: {}
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: {}
+ throttleit@2.1.0: {}
+
tiny-inflate@1.0.3: {}
tiny-invariant@1.3.3: {}
@@ -6592,6 +7542,8 @@ snapshots:
dependencies:
is-number: 7.0.0
+ tr46@0.0.3: {}
+
trim-lines@3.0.1: {}
trough@2.2.0: {}
@@ -6604,6 +7556,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 +7652,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 +7662,17 @@ 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
+
+ use-sync-external-store@1.6.0(react@19.2.3):
+ dependencies:
+ react: 19.2.3
util@0.12.5:
dependencies:
@@ -6729,13 +7699,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 +7720,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 +7733,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 +7762,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 +7881,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 +7921,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 +7939,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..4294ca9
--- /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,
+ }));
+
+ 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..1e161e0
--- /dev/null
+++ b/scripts/index-watch.ts
@@ -0,0 +1,209 @@
+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, VectorMetadata } 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 VectorMetadata,
+ }));
+
+ 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);
+
+ for (let i = 0; i < 20; i++) {
+ try {
+ await vectorIndex.delete(`${url}-${i}`);
+ } catch {
+ break;
+ }
+ }
+
+ 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..6453afc
--- /dev/null
+++ b/template/src/components/AskAnything.astro
@@ -0,0 +1,105 @@
+---
+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..5045936
--- /dev/null
+++ b/template/src/components/ChatWidget.astro
@@ -0,0 +1,297 @@
+---
+import { CHAT } from '@/types/constants';
+---
+
+
+
+
+
+
+
+
+
+
+ {CHAT.ASSISTANT_NAME}
+
+
+
+
+
+
+
+
+
+
+
{CHAT.WELCOME_MESSAGE}
+
+
+
+
+
+
+
+
+
+
diff --git a/template/src/components/Hero.astro b/template/src/components/Hero.astro
index 961a854..9cddf67 100644
--- a/template/src/components/Hero.astro
+++ b/template/src/components/Hero.astro
@@ -33,10 +33,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..ee71dda
--- /dev/null
+++ b/template/src/pages/api/chat.ts
@@ -0,0 +1,132 @@
+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;
+
+const vectorIndex = new Index({
+ url: import.meta.env.UPSTASH_VECTOR_REST_URL,
+ token: import.meta.env.UPSTASH_VECTOR_REST_TOKEN,
+});
+
+async function searchSimilarContent(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 VectorMetadata;
+ return {
+ content: `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 { 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(message);
+ const systemPrompt = buildSystemPrompt(contexts);
+
+ const genAI = new GoogleGenerativeAI(apiKey);
+ const model = genAI.getGenerativeModel({
+ model: CHAT.MODEL,
+ systemInstruction: systemPrompt,
+ });
+
+ const chatHistory: GeminiMessage[] = history.map((msg: { role: string; content: string }) => ({
+ role: msg.role === 'assistant' ? 'model' : 'user',
+ parts: [{ text: msg.content }],
+ }));
+
+ const chat = model.startChat({ history: chatHistory });
+ const result = await chat.sendMessageStream(message);
+
+ 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(`0:${JSON.stringify(text)}\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..0ff8563
--- /dev/null
+++ b/template/src/pages/chat/[uid].astro
@@ -0,0 +1,325 @@
+---
+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/constants.ts b/template/src/types/constants.ts
index 0dca849..619271c 100644
--- a/template/src/types/constants.ts
+++ b/template/src/types/constants.ts
@@ -113,3 +113,57 @@ export const LAYOUT = {
stickyTop: 'sticky top-28',
collapsedMargin: '-12rem',
} as const;
+
+export const CHAT = {
+ API_ENDPOINT: '/api/chat',
+ MODEL: 'gemini-2.5-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',
+} 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 = 'user' | 'assistant';
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';
From 5012216f5d2f7421bfb36ef2f053362220ea9405 Mon Sep 17 00:00:00 2001
From: Sithumli Nanayakkara
Date: Wed, 25 Mar 2026 22:35:41 +0530
Subject: [PATCH 2/4] feat: enhance AI chat assistant with new components, API
improvements, and security fixes
---
.changeset/ai-chat-feature.md | 9 ++--
.gitignore | 2 +
README.md | 2 +-
package.json | 6 +--
scripts/index-content.ts | 2 +-
scripts/index-watch.ts | 21 ++++----
template/src/components/AskAnything.astro | 26 ++++++----
template/src/components/ChatWidget.astro | 58 +++++++++++++++--------
template/src/components/Hero.astro | 3 --
template/src/pages/api/chat.ts | 36 ++++++++++----
template/src/pages/chat/[uid].astro | 45 +++++++++++-------
template/src/types/components.ts | 2 -
template/src/types/constants.ts | 2 +-
13 files changed, 137 insertions(+), 77 deletions(-)
diff --git a/.changeset/ai-chat-feature.md b/.changeset/ai-chat-feature.md
index 00c96c5..249761e 100644
--- a/.changeset/ai-chat-feature.md
+++ b/.changeset/ai-chat-feature.md
@@ -5,8 +5,11 @@
Add AI chat assistant with RAG-powered documentation search
- Add AskAnything component for homepage chat input
-- Add ChatWidget floating chat button for all pages
+- 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
-- Auto-index content on build
-- Add index-content:watch script for development
+- 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
diff --git a/.gitignore b/.gitignore
index a9c2bc3..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*
diff --git a/README.md b/README.md
index 2b0320e..dd26831 100644
--- a/README.md
+++ b/README.md
@@ -309,7 +309,7 @@ GOOGLE_GENERATIVE_AI_API_KEY=your_key_here
### Components
- **AskAnything**: Homepage input for starting a chat
-- **ChatWidget**: Floating chat button available on all pages
+- **ChatWidget**: Floating chat button (can be added to any layout)
## Storybook (Optional)
diff --git a/package.json b/package.json
index 0c41501..581bd4a 100644
--- a/package.json
+++ b/package.json
@@ -12,8 +12,8 @@
],
"scripts": {
"dev": "astro dev",
- "build": "pnpm run index-content && astro build && pagefind --site dist",
- "build:no-index": "astro build && pagefind --site dist",
+ "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",
@@ -42,14 +42,12 @@
"author": "",
"license": "MIT",
"dependencies": {
- "@ai-sdk/google": "^1.2.22",
"@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",
- "ai": "^4.3.19",
"astro": "^5.16.11",
"dotenv": "^16.4.0",
"fs-extra": "^11.2.0",
diff --git a/scripts/index-content.ts b/scripts/index-content.ts
index 4294ca9..aadc078 100644
--- a/scripts/index-content.ts
+++ b/scripts/index-content.ts
@@ -235,7 +235,7 @@ async function main() {
const vectors = batch.map((chunk) => ({
id: chunk.id,
data: chunk.content,
- metadata: chunk.metadata,
+ metadata: chunk.metadata as unknown as Record,
}));
await vectorIndex.upsert(vectors);
diff --git a/scripts/index-watch.ts b/scripts/index-watch.ts
index 1e161e0..862abfa 100644
--- a/scripts/index-watch.ts
+++ b/scripts/index-watch.ts
@@ -8,7 +8,7 @@ import path from 'node:path';
import { fileURLToPath } from 'node:url';
import matter from 'gray-matter';
import { Index } from '@upstash/vector';
-import type { Frontmatter, ChunkSection, VectorMetadata } from '../template/src/types/chat';
+import type { Frontmatter, ChunkSection } from '../template/src/types/chat';
import { INDEXING } from '../template/src/types/constants';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -118,7 +118,7 @@ async function indexFile(filePath: string): Promise {
url,
collection,
description: frontmatter.description as string | undefined,
- } as VectorMetadata,
+ } as Record,
}));
if (vectors.length > 0) {
@@ -130,13 +130,18 @@ async function indexFile(filePath: string): Promise {
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 < 20; i++) {
- try {
- await vectorIndex.delete(`${url}-${i}`);
- } catch {
- break;
- }
+ 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)}`);
diff --git a/template/src/components/AskAnything.astro b/template/src/components/AskAnything.astro
index 6453afc..45b06e0 100644
--- a/template/src/components/AskAnything.astro
+++ b/template/src/components/AskAnything.astro
@@ -54,6 +54,8 @@ const {
diff --git a/template/src/components/ChatWidget.astro b/template/src/components/ChatWidget.astro
index 5045936..83a1ccf 100644
--- a/template/src/components/ChatWidget.astro
+++ b/template/src/components/ChatWidget.astro
@@ -129,10 +129,19 @@ import { CHAT } from '@/types/constants';
return wrapper;
}
+ function sanitizeUrl(url) {
+ if (!url) return '';
+ var trimmed = url.trim().toLowerCase();
+ if (trimmed.startsWith('javascript:') || trimmed.startsWith('data:') || trimmed.startsWith('vbscript:')) {
+ return '';
+ }
+ return url;
+ }
+
function parseMarkdown(text) {
if (!text) return '';
- let html = text
+ var html = text
.replace(/&/g, '&')
.replace(//g, '>');
@@ -144,7 +153,11 @@ import { CHAT } from '@/types/constants';
html = html.replace(/`([^`]+)`/g, '$1');
html = html.replace(/\*\*([^*]+)\*\*/g, '$1');
html = html.replace(/\*([^*]+)\*/g, '$1');
- html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1');
+ html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, function(_, linkText, url) {
+ var safeUrl = sanitizeUrl(url);
+ if (!safeUrl) return linkText;
+ return '' + linkText + '';
+ });
html = html.replace(/\n/g, '
');
return html;
@@ -219,21 +232,14 @@ import { CHAT } from '@/types/constants';
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
- for (const line of lines) {
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
if (!line.trim()) continue;
+ if (line === 'data: [DONE]') continue;
- if (line.startsWith('0:')) {
- try {
- const text = JSON.parse(line.slice(2));
- fullContent += text;
- if (textElement) {
- textElement.innerHTML = parseMarkdown(fullContent);
- }
- scrollToBottom();
- } catch (e) {}
- } else if (line.startsWith('data: ')) {
+ if (line.startsWith('data: ')) {
try {
- const data = JSON.parse(line.slice(6));
+ var data = JSON.parse(line.slice(6));
if (data.type === 'text-delta' && data.textDelta) {
fullContent += data.textDelta;
if (textElement) {
@@ -261,16 +267,21 @@ import { CHAT } from '@/types/constants';
}
}
+ var INIT_FLAG = '__chatWidgetInitialized';
+
function initChat() {
+ if (window[INIT_FLAG]) return;
+ window[INIT_FLAG] = true;
+
if (abortController) {
abortController.abort();
}
- const trigger = document.getElementById('chat-trigger');
- const backdrop = document.getElementById('chat-backdrop');
- const closeBtn = document.getElementById('chat-close');
- const form = document.getElementById('chat-form');
- const input = document.getElementById('chat-input');
+ var trigger = document.getElementById('chat-trigger');
+ var backdrop = document.getElementById('chat-backdrop');
+ var closeBtn = document.getElementById('chat-close');
+ var form = document.getElementById('chat-form');
+ var input = document.getElementById('chat-input');
if (trigger) trigger.addEventListener('click', openModal);
if (backdrop) backdrop.addEventListener('click', closeModal);
@@ -284,14 +295,19 @@ import { CHAT } from '@/types/constants';
}
document.addEventListener('keydown', function(e) {
- const modal = document.getElementById('chat-modal');
+ var modal = document.getElementById('chat-modal');
if (e.key === 'Escape' && modal && !modal.classList.contains('hidden')) {
closeModal();
}
});
}
+ function reset() {
+ window[INIT_FLAG] = false;
+ initChat();
+ }
+
initChat();
- document.addEventListener('astro:page-load', initChat);
+ document.addEventListener('astro:page-load', reset);
})();
diff --git a/template/src/components/Hero.astro b/template/src/components/Hero.astro
index 9cddf67..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;
---
diff --git a/template/src/pages/api/chat.ts b/template/src/pages/api/chat.ts
index ee71dda..9044958 100644
--- a/template/src/pages/api/chat.ts
+++ b/template/src/pages/api/chat.ts
@@ -6,12 +6,18 @@ import { CHAT, SITE_TITLE } from '@/types/constants';
export const prerender = false;
-const vectorIndex = new Index({
- url: import.meta.env.UPSTASH_VECTOR_REST_URL,
- token: import.meta.env.UPSTASH_VECTOR_REST_TOKEN,
-});
+function getVectorIndex(): Index | null {
+ const url = import.meta.env.UPSTASH_VECTOR_REST_URL;
+ const token = import.meta.env.UPSTASH_VECTOR_REST_TOKEN;
-async function searchSimilarContent(query: string, topK: number = CHAT.VECTOR_TOP_K): Promise {
+ 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,
@@ -20,9 +26,10 @@ async function searchSimilarContent(query: string, topK: number = CHAT.VECTOR_TO
});
return results.map((result) => {
- const metadata = result.metadata as VectorMetadata;
+ const metadata = result.metadata as unknown as VectorMetadata;
+ const chunkContent = typeof result.data === 'string' ? result.data : '';
return {
- content: `Title: ${metadata.title}\nSection: ${metadata.section}\nURL: ${metadata.url}`,
+ content: chunkContent || `Title: ${metadata.title}\nSection: ${metadata.section}\nURL: ${metadata.url}`,
title: metadata.title,
section: metadata.section,
url: metadata.url,
@@ -65,6 +72,16 @@ export const POST: APIRoute = async ({ request }) => {
});
}
+ 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') {
@@ -74,7 +91,7 @@ export const POST: APIRoute = async ({ request }) => {
});
}
- const contexts = await searchSimilarContent(message);
+ const contexts = await searchSimilarContent(vectorIndex, message);
const systemPrompt = buildSystemPrompt(contexts);
const genAI = new GoogleGenerativeAI(apiKey);
@@ -98,9 +115,10 @@ export const POST: APIRoute = async ({ request }) => {
for await (const chunk of result.stream) {
const text = chunk.text();
if (text) {
- controller.enqueue(encoder.encode(`0:${JSON.stringify(text)}\n`));
+ 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);
diff --git a/template/src/pages/chat/[uid].astro b/template/src/pages/chat/[uid].astro
index 0ff8563..9e88875 100644
--- a/template/src/pages/chat/[uid].astro
+++ b/template/src/pages/chat/[uid].astro
@@ -125,10 +125,19 @@ const { uid } = Astro.params;
return wrapper;
}
+ function sanitizeUrl(url) {
+ if (!url) return '';
+ var trimmed = url.trim().toLowerCase();
+ if (trimmed.startsWith('javascript:') || trimmed.startsWith('data:') || trimmed.startsWith('vbscript:')) {
+ return '';
+ }
+ return url;
+ }
+
function parseMarkdown(text) {
if (!text) return '';
- let html = text
+ var html = text
.replace(/&/g, '&')
.replace(//g, '>');
@@ -147,8 +156,12 @@ const { uid } = Astro.params;
// Italic
html = html.replace(/\*([^*]+)\*/g, '$1');
- // Links
- html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1');
+ // Links - with URL sanitization
+ html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, function(_, linkText, url) {
+ var safeUrl = sanitizeUrl(url);
+ if (!safeUrl) return linkText;
+ return '' + linkText + '';
+ });
// Line breaks
html = html.replace(/\n/g, '
');
@@ -172,14 +185,10 @@ const { uid } = Astro.params;
function setLoading(loading) {
isStreaming = loading;
- const submitBtn = document.getElementById('chat-submit');
- const sendIcon = document.getElementById('send-icon');
- const loadingIcon = document.getElementById('loading-icon');
- const input = document.getElementById('chat-input');
+ var submitBtn = document.getElementById('chat-submit');
+ var input = document.getElementById('chat-input');
if (submitBtn) submitBtn.disabled = loading;
- if (sendIcon) sendIcon.classList.toggle('hidden', loading);
- if (loadingIcon) loadingIcon.classList.toggle('hidden', !loading);
if (input) input.disabled = loading;
}
@@ -249,17 +258,21 @@ const { uid } = Astro.params;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
- for (const line of lines) {
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
if (!line.trim()) continue;
+ if (line === 'data: [DONE]') continue;
- if (line.startsWith('0:')) {
+ if (line.startsWith('data: ')) {
try {
- const text = JSON.parse(line.slice(2));
- fullContent += text;
- if (textElement) {
- textElement.innerHTML = parseMarkdown(fullContent);
+ var data = JSON.parse(line.slice(6));
+ if (data.type === 'text-delta' && data.textDelta) {
+ fullContent += data.textDelta;
+ if (textElement) {
+ textElement.innerHTML = parseMarkdown(fullContent);
+ }
+ scrollToBottom();
}
- scrollToBottom();
} catch (e) {
// Skip malformed JSON
}
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 619271c..a806667 100644
--- a/template/src/types/constants.ts
+++ b/template/src/types/constants.ts
@@ -166,4 +166,4 @@ export const CHAT_ROLES = {
model: 'model',
} as const;
-export type ChatRole = 'user' | 'assistant';
+export type ChatRole = (typeof CHAT_ROLES)[keyof typeof CHAT_ROLES];
From 93391bf5f3d2fa1d4588ede17858dd3890c0f45b Mon Sep 17 00:00:00 2001
From: Sithumli Nanayakkara
Date: Wed, 25 Mar 2026 22:39:05 +0530
Subject: [PATCH 3/4] feat: remove unused AI SDK dependencies from
pnpm-lock.yaml
---
pnpm-lock.yaml | 157 -------------------------------------------------
1 file changed, 157 deletions(-)
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d03680e..a5bed2f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,9 +8,6 @@ importers:
.:
dependencies:
- '@ai-sdk/google':
- specifier: ^1.2.22
- version: 1.2.22(zod@3.25.76)
'@astrojs/check':
specifier: ^0.9.6
version: 0.9.6(prettier@3.8.1)(typescript@5.9.3)
@@ -29,9 +26,6 @@ importers:
'@upstash/vector':
specifier: ^1.1.0
version: 1.2.3
- ai:
- specifier: ^4.3.19
- version: 4.3.19(react@19.2.3)(zod@3.25.76)
astro:
specifier: ^5.16.11
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)
@@ -114,38 +108,6 @@ packages:
'@adobe/css-tools@4.4.4':
resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==}
- '@ai-sdk/google@1.2.22':
- resolution: {integrity: sha512-Ppxu3DIieF1G9pyQ5O1Z646GYR0gkC57YdBqXJ82qvCdhEhZHu0TWhmnOoeIWe2olSbuDeoOY+MfJrW8dzS3Hw==}
- engines: {node: '>=18'}
- peerDependencies:
- zod: ^3.0.0
-
- '@ai-sdk/provider-utils@2.2.8':
- resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==}
- engines: {node: '>=18'}
- peerDependencies:
- zod: ^3.23.8
-
- '@ai-sdk/provider@1.1.3':
- resolution: {integrity: sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==}
- engines: {node: '>=18'}
-
- '@ai-sdk/react@1.2.12':
- resolution: {integrity: sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==}
- engines: {node: '>=18'}
- peerDependencies:
- react: ^18 || ^19 || ^19.0.0-rc
- zod: ^3.23.8
- peerDependenciesMeta:
- zod:
- optional: true
-
- '@ai-sdk/ui-utils@1.2.11':
- resolution: {integrity: sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==}
- engines: {node: '>=18'}
- peerDependencies:
- zod: ^3.23.8
-
'@astrojs/check@0.9.6':
resolution: {integrity: sha512-jlaEu5SxvSgmfGIFfNgcn5/f+29H61NJzEMfAZ82Xopr4XBchXB1GVlcJsE+elUlsYSbXlptZLX+JMG3b/wZEA==}
hasBin: true
@@ -827,10 +789,6 @@ packages:
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
- '@opentelemetry/api@1.9.0':
- resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
- engines: {node: '>=8.0.0'}
-
'@oslojs/encoding@1.1.0':
resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==}
@@ -1308,9 +1266,6 @@ packages:
'@types/deep-eql@4.0.2':
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
- '@types/diff-match-patch@1.0.36':
- resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==}
-
'@types/estree-jsx@1.0.5':
resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
@@ -1508,16 +1463,6 @@ packages:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
- ai@4.3.19:
- resolution: {integrity: sha512-dIE2bfNpqHN3r6IINp9znguYdhIOheKW2LDigAMrgt/upT3B8eBGPSCblENvaZGoq+hxaN9fSMzjWpbqloP+7Q==}
- engines: {node: '>=18'}
- peerDependencies:
- react: ^18 || ^19 || ^19.0.0-rc
- zod: ^3.23.8
- peerDependenciesMeta:
- react:
- optional: true
-
ajv-draft-04@1.0.0:
resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==}
peerDependencies:
@@ -1858,9 +1803,6 @@ packages:
devlop@1.1.0:
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
- diff-match-patch@1.0.5:
- resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==}
-
diff@8.0.3:
resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==}
engines: {node: '>=0.3.1'}
@@ -2378,20 +2320,12 @@ packages:
json-schema-traverse@1.0.0:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
- json-schema@0.4.0:
- resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
-
jsonc-parser@2.3.1:
resolution: {integrity: sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==}
jsonc-parser@3.3.1:
resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==}
- jsondiffpatch@0.6.0:
- resolution: {integrity: sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==}
- engines: {node: ^18.0.0 || >=20.0.0}
- hasBin: true
-
jsonfile@4.0.0:
resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
@@ -3108,9 +3042,6 @@ packages:
resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==}
engines: {node: '>=4'}
- secure-json-parse@2.7.0:
- resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==}
-
semver@7.7.3:
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
engines: {node: '>=10'}
@@ -3242,11 +3173,6 @@ packages:
engines: {node: '>=16'}
hasBin: true
- swr@2.4.1:
- resolution: {integrity: sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA==}
- peerDependencies:
- react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
-
tailwindcss@4.1.18:
resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==}
@@ -3262,10 +3188,6 @@ packages:
resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==}
engines: {node: '>=8'}
- throttleit@2.1.0:
- resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==}
- engines: {node: '>=18'}
-
tiny-inflate@1.0.3:
resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==}
@@ -3481,11 +3403,6 @@ packages:
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
- use-sync-external-store@1.6.0:
- resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
- peerDependencies:
- react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
-
util@0.12.5:
resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==}
@@ -3799,40 +3716,6 @@ snapshots:
'@adobe/css-tools@4.4.4': {}
- '@ai-sdk/google@1.2.22(zod@3.25.76)':
- dependencies:
- '@ai-sdk/provider': 1.1.3
- '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76)
- zod: 3.25.76
-
- '@ai-sdk/provider-utils@2.2.8(zod@3.25.76)':
- dependencies:
- '@ai-sdk/provider': 1.1.3
- nanoid: 3.3.11
- secure-json-parse: 2.7.0
- zod: 3.25.76
-
- '@ai-sdk/provider@1.1.3':
- dependencies:
- json-schema: 0.4.0
-
- '@ai-sdk/react@1.2.12(react@19.2.3)(zod@3.25.76)':
- dependencies:
- '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76)
- '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76)
- react: 19.2.3
- swr: 2.4.1(react@19.2.3)
- throttleit: 2.1.0
- optionalDependencies:
- zod: 3.25.76
-
- '@ai-sdk/ui-utils@1.2.11(zod@3.25.76)':
- dependencies:
- '@ai-sdk/provider': 1.1.3
- '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76)
- zod: 3.25.76
- zod-to-json-schema: 3.25.1(zod@3.25.76)
-
'@astrojs/check@0.9.6(prettier@3.8.1)(typescript@5.9.3)':
dependencies:
'@astrojs/language-server': 2.16.3(prettier@3.8.1)(typescript@5.9.3)
@@ -4533,8 +4416,6 @@ snapshots:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.20.1
- '@opentelemetry/api@1.9.0': {}
-
'@oslojs/encoding@1.1.0': {}
'@pagefind/darwin-arm64@1.4.0':
@@ -5005,8 +4886,6 @@ snapshots:
'@types/deep-eql@4.0.2': {}
- '@types/diff-match-patch@1.0.36': {}
-
'@types/estree-jsx@1.0.5':
dependencies:
'@types/estree': 1.0.8
@@ -5240,18 +5119,6 @@ snapshots:
agent-base@7.1.4: {}
- ai@4.3.19(react@19.2.3)(zod@3.25.76):
- dependencies:
- '@ai-sdk/provider': 1.1.3
- '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76)
- '@ai-sdk/react': 1.2.12(react@19.2.3)(zod@3.25.76)
- '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76)
- '@opentelemetry/api': 1.9.0
- jsondiffpatch: 0.6.0
- zod: 3.25.76
- optionalDependencies:
- react: 19.2.3
-
ajv-draft-04@1.0.0(ajv@8.17.1):
optionalDependencies:
ajv: 8.17.1
@@ -5646,8 +5513,6 @@ snapshots:
dependencies:
dequal: 2.0.3
- diff-match-patch@1.0.5: {}
-
diff@8.0.3: {}
dir-glob@3.0.1:
@@ -6298,18 +6163,10 @@ snapshots:
json-schema-traverse@1.0.0: {}
- json-schema@0.4.0: {}
-
jsonc-parser@2.3.1: {}
jsonc-parser@3.3.1: {}
- jsondiffpatch@0.6.0:
- dependencies:
- '@types/diff-match-patch': 1.0.36
- chalk: 5.6.2
- diff-match-patch: 1.0.5
-
jsonfile@4.0.0:
optionalDependencies:
graceful-fs: 4.2.11
@@ -7325,8 +7182,6 @@ snapshots:
extend-shallow: 2.0.1
kind-of: 6.0.3
- secure-json-parse@2.7.0: {}
-
semver@7.7.3: {}
set-function-length@1.2.2:
@@ -7491,12 +7346,6 @@ snapshots:
picocolors: 1.1.1
sax: 1.4.4
- swr@2.4.1(react@19.2.3):
- dependencies:
- dequal: 2.0.3
- react: 19.2.3
- use-sync-external-store: 1.6.0(react@19.2.3)
-
tailwindcss@4.1.18: {}
tapable@2.3.0: {}
@@ -7511,8 +7360,6 @@ snapshots:
term-size@2.2.1: {}
- throttleit@2.1.0: {}
-
tiny-inflate@1.0.3: {}
tiny-invariant@1.3.3: {}
@@ -7670,10 +7517,6 @@ snapshots:
punycode: 2.3.1
optional: true
- use-sync-external-store@1.6.0(react@19.2.3):
- dependencies:
- react: 19.2.3
-
util@0.12.5:
dependencies:
inherits: 2.0.4
From aa31e2622bc2589035e502b9e3cc3bd04052c499 Mon Sep 17 00:00:00 2001
From: Sithumli Nanayakkara
Date: Wed, 25 Mar 2026 23:11:06 +0530
Subject: [PATCH 4/4] feat: implement rate limit handling and improved error
messages in chat functionality
---
template/src/components/ChatWidget.astro | 9 +++++-
template/src/pages/api/chat.ts | 41 ++++++++++++++++++++----
template/src/pages/chat/[uid].astro | 11 ++++++-
template/src/types/constants.ts | 3 +-
4 files changed, 54 insertions(+), 10 deletions(-)
diff --git a/template/src/components/ChatWidget.astro b/template/src/components/ChatWidget.astro
index 83a1ccf..49433c2 100644
--- a/template/src/components/ChatWidget.astro
+++ b/template/src/components/ChatWidget.astro
@@ -218,6 +218,10 @@ import { CHAT } from '@/types/constants';
});
if (!response.ok) {
+ if (response.status === 429) {
+ const data = await response.json();
+ throw new Error(data.message || "I've reached my daily limit. Please try again tomorrow!");
+ }
throw new Error('Failed to get response');
}
@@ -255,12 +259,15 @@ import { CHAT } from '@/types/constants';
if (fullContent) {
chatHistory.push({ role: 'assistant', content: fullContent });
}
+ scrollToBottom();
} catch (error) {
if (error.name === 'AbortError') return;
console.error('Chat error:', error);
+ const errorMessage = error instanceof Error ? error.message : 'Sorry, an error occurred. Please try again.';
if (textElement) {
- textElement.innerHTML = 'Sorry, an error occurred. Please try again.';
+ textElement.innerHTML = '' + errorMessage + '';
}
+ scrollToBottom();
} finally {
setLoading(false);
abortController = null;
diff --git a/template/src/pages/api/chat.ts b/template/src/pages/api/chat.ts
index 9044958..59076fe 100644
--- a/template/src/pages/api/chat.ts
+++ b/template/src/pages/api/chat.ts
@@ -95,18 +95,45 @@ export const POST: APIRoute = async ({ request }) => {
const systemPrompt = buildSystemPrompt(contexts);
const genAI = new GoogleGenerativeAI(apiKey);
- const model = genAI.getGenerativeModel({
- model: CHAT.MODEL,
- systemInstruction: systemPrompt,
- });
-
const chatHistory: GeminiMessage[] = history.map((msg: { role: string; content: string }) => ({
role: msg.role === 'assistant' ? 'model' : 'user',
parts: [{ text: msg.content }],
}));
- const chat = model.startChat({ history: chatHistory });
- const result = await chat.sendMessageStream(message);
+ 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({
diff --git a/template/src/pages/chat/[uid].astro b/template/src/pages/chat/[uid].astro
index 9e88875..e6949fd 100644
--- a/template/src/pages/chat/[uid].astro
+++ b/template/src/pages/chat/[uid].astro
@@ -239,6 +239,10 @@ const { uid } = Astro.params;
if (loadingMsg) loadingMsg.remove();
if (!response.ok) {
+ if (response.status === 429) {
+ const data = await response.json();
+ throw new Error(data.message || "I've reached my daily limit. Please try again tomorrow!");
+ }
throw new Error('Failed to get response');
}
@@ -284,13 +288,18 @@ const { uid } = Astro.params;
if (fullContent) {
chatHistory.push({ role: 'assistant', content: fullContent });
}
+
+ // Final scroll to ensure complete message is visible
+ scrollToBottom();
} catch (error) {
console.error('Chat error:', error);
const loadingMsg = document.getElementById('loading-message');
if (loadingMsg) loadingMsg.remove();
- const errorMsg = createMessageElement('assistant', 'Sorry, an error occurred. Please try again.');
+ const errorMessage = error instanceof Error ? error.message : 'Sorry, an error occurred. Please try again.';
+ const errorMsg = createMessageElement('assistant', errorMessage);
messagesContainer.appendChild(errorMsg);
+ scrollToBottom();
} finally {
setLoading(false);
// Focus input after response
diff --git a/template/src/types/constants.ts b/template/src/types/constants.ts
index a806667..47fc957 100644
--- a/template/src/types/constants.ts
+++ b/template/src/types/constants.ts
@@ -116,11 +116,12 @@ export const LAYOUT = {
export const CHAT = {
API_ENDPOINT: '/api/chat',
- MODEL: 'gemini-2.5-flash',
+ 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 = {